SteelWorks ships with three test layers that give you fast feedback at every level of the stack. Unit tests run in milliseconds with no external dependencies, integration tests verify real SQL against a live PostgreSQL instance, and Playwright end-to-end tests drive a full browser session against a locally running Streamlit app. All three layers are discovered by a singleDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/kishnahai0806/SteelWorks/llms.txt
Use this file to discover all available pages before exploring further.
pytest invocation.
Setup
Install project and dev dependencies
Use Poetry to install everything, including pytest, Playwright, and psycopg:
Create .env.test
Integration and E2E tests read the test database URL from The
.env.test in the repository root. Create the file with the default local connection string:DATABASE_URL_TEST key is also accepted as a fallback for slide compatibility.If both
TEST_DATABASE_URL and DATABASE_URL_TEST are absent, the test suite falls back to the default local URL (postgresql+pg8000://devuser:devpass@127.0.0.1:5433/testdb) and attempts to start a Docker container automatically. Unit tests always run regardless.Running tests
- All tests
- Unit only
- Integration only
- E2E only
Run every layer — unit, integration, and E2E — in a single command:
Unit tests
Unit tests live intests/unit/test_operations_service.py. They instantiate OperationsMetricsService() without passing a repository, which activates the service’s built-in in-memory fallback data (2 weeks, 3 lines, 6 issue occurrences). No database, no Docker, no network — the full suite runs in under a second.
| Test function | What it verifies |
|---|---|
test_normalize_line_ids_deduplicates_and_sorts | Passing [4, 1, 4, 2, 1] returns [1, 2, 4] — duplicates dropped, result sorted |
test_get_available_weeks_returns_selectable_values | get_available_weeks() returns exactly 2026-W03 and 2026-W04 |
test_get_available_lines_returns_selectable_values | get_available_lines() returns only active lines (Line 2 is inactive and excluded) |
test_get_issue_summary_uses_selection_scope | Grouped issue summary for week 1, lines [1,2,3] returns correct per-line counts |
test_get_affected_lots_returns_lot_level_rows | Lot-level aggregation for week 1, line 1 yields LOT-1001 (tool_wear) and LOT-1002 (material_shortage) |
test_export_issue_summary_csv_matches_display_scope | Exported bytes decode to valid CSV with correct headers and 2026-W03 rows |
test_export_affected_lots_csv_matches_display_scope | Exported bytes include the week_label,line_name,lot_code,issue_count,issue_types header and a matching data row |
test_get_issue_summary_logs_selected_scope | Logging at INFO level captures "Issue summary generated from fallback data", week_id=1, and line_count=2 |
test_get_affected_lots_logs_when_no_lines_selected | Calling get_affected_lots with an empty production_line_ids list returns [] and logs "Affected lots requested with no selected lines" |
Integration tests
Integration tests live intests/integration/test_operations_service_integration.py. They exercise OperationsMetricsService wired to a real OperationsRepository connected to a live PostgreSQL instance.
Docker container management is handled automatically by the shared conftest.py fixture. When TEST_DATABASE_URL points to 127.0.0.1:5433/testdb, the fixture:
- Removes any existing container named
postgres18 - Starts a fresh
postgres:18container on port5433 - Waits up to 60 seconds for the database to accept connections
- Drops and recreates the schema from
db/schema.sql - Seeds test data from
db/seed.sql - Tears down the container after the session ends
TEST_DATABASE_URL points to a remote host, the fixture runs the same reset/seed cycle but skips Docker management. As a safety guard, any non-local URL whose database name does not contain the word test raises a RuntimeError to prevent accidentally wiping production data.
The three integration tests cover the following scenarios:
| Test function | What it verifies |
|---|---|
test_get_available_filters_from_database | Weeks list is ["2026-W03", "2026-W04"]; lines list is ["Line 1", "Line 2", "Line 4"] against the seeded database |
test_issue_summary_grouped_by_line_uses_selected_scope | Issue summary for week 1, lines 1 and 4 matches expected (line_name, issue_type_name, issue_count) tuples exactly |
test_affected_lots_and_exports_match_database_scope | Affected lots for week 2, line 2 returns LOT-2001 and LOT-2002; CSV exports contain correct headers and data rows |
Integration and E2E tests both read
TEST_DATABASE_URL from .env.test. If neither TEST_DATABASE_URL nor DATABASE_URL_TEST is set, the suite falls back to the default local URL and tries to start a Docker container. Unit tests are never skipped.db_service fixture that the integration tests depend on is defined in tests/conftest.py:
E2E tests
End-to-end tests live intests/e2e/test_streamlit_dashboard.py. They use the Playwright Page object to control a real browser against a locally running Streamlit process.
The streamlit_server_url session fixture in tests/e2e/conftest.py starts the app automatically:
- Depends on
prepared_test_database, so the Docker container and seed data are ready before the browser opens - Launches
streamlit run streamlit_app.py --server.port=8510 --server.headless=true - Passes
DATABASE_URLfrom.env.testso the app queries the same seeded data as the integration tests - Polls
http://127.0.0.1:8510until a200response is received (up to 60 seconds) - Terminates the Streamlit process after the session ends
test_dashboard_loads_seeded_data, navigates to the app and asserts:
- The
"Operations Issue Metrics"heading is visible - The
"Issue Summary"section heading is visible - The row count labels
"Issue summary rows: 3"and"Affected lots rows: 3"are present - Both
"Download issue summary CSV"and"Download affected lots CSV"buttons are visible