Integration Test Specifications
| Type | Level | MID | UID | REFS | Title | Result | Statement | Rationale | Comment | TEST_METHOD | TEST_TOOL | ACCEPTANCE_CRITERIA | STATUS | REVIEWED_HASH | REVIEWED_BY | QMS_REVIEW_DATE | NOTES |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| REQUIREMENT | 1 | IT-001 | Parents: | First-launch DB initialisation | Passed | Instantiate AppDatabase against a temporary filesystem path at which no database file exists. Trigger the initialisation pathway by performing any query that forces Drift's onCreate migration to run. Inspect the resulting state of the database on disk and of the seeded tables. |
automated | flutter_test | The database file exists at the temporary path after the first query. PRAGMA user_version on the opened database equals the value of AppDatabase.schemaVersion declared in application code. The clinics table contains at least one row whose origin is "local", confirming that the bootstrap seed step executed. | Approved | f3983783ae35415f11933728d91e2f1136516b980063f2d16dedacf6d32b4c47 | @DougYoungberg | Test harness couples to the StrictDoc item via tags: ['IT-001'] on the flutter_test test entry. Test file location: test/integration/first_launch_db_initialization_integration_test.dart. Verifies the architectural integration of the local persistence subsystem (ARCH-001); contributes evidence to SRS-001 / SYS-001 indirectly via the typed-link graph. | ||||
| REQUIREMENT | 2 | IT-002 | Parents: | Database file is encrypted at rest | Passed | Initialise an AppDatabase against a temporary filesystem path through the production openEncryptedConnection pathway with a known test key. Write a canonical row through the typed clinic API and close the database. Attempt to open the resulting file with the plain sqlite3 driver (no credential supplied) and read any user table. Attempt to open the same file through the production pathway with an incorrect key and read any user table. Then re-open the file through the production pathway with the correct key and read the canonical row. |
automated | flutter_test | The unkeyed open and the wrong-key open both return a SQLCipher error (typically "file is not a database" or "file is encrypted"), demonstrating the file cannot be read with any arbitrary key. The correct-key open succeeds and the canonical row written before close is readable. | Approved | 66f90f4e85e357a9c6e4cd2a2ee9f83e20f87c4f8f909318847148c7f44060c9 | @DougYoungberg | Test name carries the UID prefix (IT-002) so the Flutter/JUnit join channel resolves. Test file location: test/integration/database_encryption_at_rest_integration_test.dart (distinct from test/integration/encryption_test.dart, which covers EncryptionService primitives only). | ||||
| REQUIREMENT | 3 | IT-003 | Parents: | Database integrity mechanisms reject corrupt writes and roll back partial state | Passed | Initialise an AppDatabase against a temporary filesystem path through the production openEncryptedConnection pathway with a known test key, forcing first-launch onCreate so the seeded LOCAL_CLINIC and the schema (including foreign-key declarations) are in place. Then, in four independent scenarios, exercise each of the rejection categories named in SRS-003 / ARCH-003. (a) Foreign-key enforcement — attempt to insert a patient row whose clinic_id references a clinic that does not exist, and verify the SQLite engine rejects the write. (b) Transactional atomicity — open a Drift transaction that performs two writes (insert a new clinic, then insert a patient referencing that clinic), then throw an exception inside the transaction; re-open the database and verify neither row is present. (c) CHECK constraint enforcement — attempt to insert a clinic row whose origin column value is outside the enumerated set ('local', 'remote', 'cloud') declared on the schema, and verify the SQLite engine rejects the write. (d) NOT NULL constraint enforcement — issue a raw INSERT (via customStatement, bypassing Drift's typed companion which guards NOT NULL at compile time) that supplies NULL for the clinics.name column, and verify the SQLite engine rejects the write at runtime. Bypassing Drift is necessary to prove the engine — not the compile-time guard — is what protects the data when a future migration, an unsafe raw write, or another customStatement caller bypasses the type system. |
automated | flutter_test | (a) The orphan-patient insert raises a SqliteException whose message contains "FOREIGN KEY constraint failed" (case-insensitive) and the patients table contains no row with the orphan local_patient_id after the failed call. (b) After the throwing transaction completes and the database is re-opened through the same encrypted pathway, neither the new clinic row nor the new patient row inserted inside the transaction is present in their respective tables. (c) The invalid-origin clinic insert raises a SqliteException whose message contains "CHECK constraint failed" (case-insensitive) and the clinics table contains no row with the rejected clinic_id. (d) The raw NULL-name insert raises a SqliteException whose message contains "NOT NULL constraint failed" (case-insensitive) and the clinics table contains no row with the rejected clinic_id. The original seeded LOCAL_CLINIC remains visible throughout all four scenarios, demonstrating that the failed writes did not collaterally damage pre-existing rows. | Approved | 668ffee93cca42e8d4c968988c9e417caace6e8172edb7a9f40cc86de4bd6c56 | @DougYoungberg | Test file location: test/integration/database_integrity_integration_test.dart. Each scenario runs as a separate flutter_test case with its own temp directory for failure isolation. The test exercises the production openEncryptedConnection pathway, so removing the PRAGMA foreign_keys = ON statement, regressing Drift's FK-bearing schema, or weakening the transaction wrapper would be visible as IT-003 failures. Coverage strategy: each rejection category named in SRS-003 (FOREIGN KEY, CHECK, NOT NULL) is exercised by one representative case rather than enumerating every FK relation or every CHECK-bound column in the schema. The same SQLite enforcement engine handles all FK constraints and all CHECK columns identically — proving the mechanism on one example proves it for all. This representative-coverage approach is appropriate for IEC 62304 Class B integration testing; if BioFlow is reclassified to Class C, an exhaustive matrix of (relation x constraint type) would be required. | ||||
| REQUIREMENT | 4 | IT-004 | Parents: | Audit-log entries are written for each clinical record operation | Passed | Initialise an AppDatabase against a temporary filesystem path through the production encrypted-connection pathway with a known test key, forcing first-launch onCreate so the schema and seeded LOCAL_CLINIC are in place. Construct the use cases or the production callers that participate in the audit chain, wired with a real AuditService backed by the production audit-repository adapter against the same database. Then, in seven independent scenarios, exercise the SRS-004 commitment that each named operation writes its audit entry. (a) Patient creation: invoke the create-patient use case for a fresh patient; query audit_logs for entries on that patient; verify exactly one entry of action "PATIENT_CREATED" exists with the patient's identifier and a non-null timestamp. (b) Patient update: create a patient (setup), invoke the update-patient use case with a changed field; verify exactly one entry of action "PATIENT_UPDATED" exists with the patient's identifier. (c) Patient deletion: create a patient (setup), invoke the delete-patient use case; verify exactly one entry of action "PATIENT_DELETED" exists with the patient's identifier. (d) Recording start: drive the recording-start pathway through the production recording-notifier (with the signal-processing engine and sleep-prevention port replaced by test doubles); verify one entry of action "RECORDING_STARTED" exists with the recording identifier. (e) Recording stop: drive the recording-start then recording-stop pathways through the production recording-notifier with test doubles for the heavy collaborators; verify one entry of action "RECORDING_STOPPED" exists with the recording identifier (in addition to the "RECORDING_STARTED" entry). (f) Recording deletion: create a recording (setup), invoke the delete-recording use case; verify one entry of action "RECORDING_DELETED" exists with the recording identifier. (g) Upload: insert a recording (setup), drive the upload pathway through the production upload-manager with a test stub for the cloud client; verify one entry of action "RECORDING_UPLOADED" exists with the recording identifier. |
automated | flutter_test | For each scenario: the corresponding audit_logs row exists after the operation, with the expected action string, the expected entity identifier in entity_id, and a non-null UTC timestamp. The seeded LOCAL_CLINIC and any unrelated rows remain unchanged. | Approved | f1d13532cf6bb1be63bb95f47499f5c328ddb234b2072e85f072ac4734cfd34f | @DougYoungberg | Test file location: test/integration/audit_log_integration_test.dart. Each scenario runs as a separate flutter_test case with its own temp directory for failure isolation. The test exercises the production code that ultimately invokes the audit service, so removing the audit call from any use case, the recording-notifier, or the upload-manager would be visible as a missing-entry failure on the corresponding scenario. Coverage strategy: each scenario verifies that a specific audited operation produces the SRS-004-required entry. Representative coverage is appropriate for the device's current software-safety classification; if the device is reclassified upward, an exhaustive matrix across time-zone edge cases and persistence failure modes would be required. | ||||
| REQUIREMENT | 5 | IT-005 | Parents: | Audit-log records have the required shape and are immutable | Passed | Initialise an AppDatabase against a temporary filesystem path through the production encrypted-connection pathway with a known test key. Construct the audit chain (AuditService + production audit-repository adapter against the same database). Then, in two scenarios, verify the ARCH-005 commitments on the audit_logs row shape and the SRS-004 immutability rule. (a) Record shape: write a known audit entry via the audit service and query audit_logs for it; verify the row has a non-empty audit identifier, an action string drawn from the application's enumerated audit actions, the expected entity_type and entity_id, a non-null UTC timestamp generated by the database (not by the caller), and a parseable JSON details payload. (b) Write-then-read sanity check: write an audit entry via the audit service, re-read it, and assert the details payload and system timestamp match what was written. Immutability is enforced structurally — the audit-repository port (SRS-004 / ARCH-005) exposes only create-and-read methods, so no application path can mutate a row. This scenario is the runtime corollary, confirming that the storage layer round-trips an entry faithfully. |
automated | flutter_test | (a) The queried audit_logs row carries: a non-empty audit identifier, an action drawn from the enumerated audit actions, the entity_type and entity_id supplied to the audit service, a timestamp within a small tolerance of the test wall-clock at insert (demonstrating database-side generation), and a JSON details payload that round-trips through a JSON decoder. (b) Re-reading the written entry returns the same JSON-serialised details payload and the same timestamp as the original write. | Approved | 5dd9f184de1f449f74826c5449c666ed8a06cc7b84486d3cf415c0b9940dda06 | @DougYoungberg | Test file location: test/integration/audit_log_record_shape_integration_test.dart. Each scenario runs as a separate flutter_test case with its own temp directory for failure isolation. Immutability is enforced structurally at the port surface: IAuditRepositoryPort exposes only create-and-read operations, so no application path can change a written entry. Code review of the port shape is the primary defence; IT-005 (b) is the runtime sanity check that storage round-trips entries faithfully. | ||||
| REQUIREMENT | 6 | IT-006 | Parents: | Audit-trail export produces a faithful copy of the on-device entries | Passed | Initialise an AppDatabase against a temporary filesystem path through the production encrypted-connection pathway with a known test key. Construct the audit chain (AuditService + production audit-repository adapter against the same database) and exercise two distinct audited operations through their use cases (a patient creation and a patient deletion) so that at least two distinguishable entries are present in audit_logs. Then, in two scenarios, exercise the SRS-005 / ARCH-006 commitment that the review-and-export surface produces a faithful copy of the trail. (a) CSV export: invoke the production export pathway requesting CSV output to a temporary file path; re-open the file with a CSV reader and assert that each row in audit_logs has a corresponding row in the file with the same audit_id, action, entity_type, entity_id, user_id, timestamp, and details content. (b) JSON export: invoke the production export pathway requesting JSON output to a temporary file path; re-open the file with a JSON parser and assert that each row in audit_logs has a corresponding object in the resulting array with the same audit_id, action, entity_type, entity_id, user_id, timestamp, and details content. |
automated | flutter_test | (a) The CSV file exists at the requested path, parses without error, contains one data row per audit_logs row, and each row's fields match the source row's values after CSV unescape. (b) The JSON file exists at the requested path, parses without error as an array, contains one object per audit_logs row, and each object's fields match the source row's values exactly. For both scenarios: no audit_logs rows are missing from the export, and the export does not introduce entries not present in audit_logs. | Approved | a102a8238f8bb687ac942b3c3b884f9d38525563cad6a13c569d2541b05c464f | @DougYoungberg | Test file location: test/integration/audit_log_export_integration_test.dart. Each scenario runs as a separate flutter_test case with its own temp directory for failure isolation. The test exercises the production export code path, so a serialisation regression (omitting a field, dropping a row, mis-formatting a timestamp) would be visible as either a parse failure or a field-mismatch failure on the corresponding scenario. Coverage strategy: each scenario verifies one export format end-to-end. The fields verified are the fields named in ARCH-006 (audit_id, timestamp, action, entity_type, entity_id, user_id, details). Representative coverage across two formats is appropriate for the device's current software-safety classification; if the device is reclassified upward, an exhaustive matrix across timestamp edge cases, multi-byte detail payloads, and very large exports would be required. | ||||
| REQUIREMENT | 7 | IT-008 | Parents: | Recording widget renders controls and context | Pump the RecordingWidgetView — the rendering view that the provider-bound RecordingWidget container drives — in a flutter_test widget test with a seeded RecordingWidgetState representing a recording in progress for a known patient with a known elapsed time. Render the view and inspect its widget tree. |
automated | flutter_test | The view renders without error. It displays the seeded patient name, the elapsed-recording-time readout in HH:MM:SS form matching the seeded duration, and a recording control (the pause control shown while a recording is active). | Approved | c8989a1ef86c501d3791f6dcf8940e4043b0fdc0e2018e989fe5cd746a9db218 | @DougYoungberg | Test file: test/widget/recording_widget_test.dart, in the IT-008-prefixed test case (the test name carries the UID prefix for the Flutter/JUnit join channel). The test exercises RecordingWidgetView, which the provider-bound RecordingWidget container renders from application state (ARCH-008); testing the view directly avoids standing up the container's full provider graph. The always-visible-across-states commitment of SRS-007 is observed end-to-end in the running application by ST-005. | |||||
| REQUIREMENT | 8 | IT-009 | Parents: | Bottom bar switches rendered mode on toggle | Passed | Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle. Inspect the rendered controls again, then activate the toggle a second time and inspect once more. |
automated | flutter_test | In the default (signal) mode the bottom bar renders the signal-parameter controls. After the mode toggle is activated, the bottom bar renders the menu-mode controls (the Patients, Montages, Settings, and Activities navigation items). Activating the toggle a second time returns the bar to the signal-mode controls. | Approved | 7d9d0fd6c986e6bb3721765d6ba65507de9c5bc356c0edd45cf30716d2d04f99 | @DougYoungberg | Test file: test/integration/bottom_bar_integration_test.dart, in the IT-009-prefixed test case (the test name carries the UID prefix for the Flutter/JUnit join channel). Verifies the architectural integration of the bottom-bar mode controller (ARCH-009) — the widget-to-view-model wiring that switches the rendered controls between signal and menu mode. Contributes evidence to SRS-008 via the typed-link graph; ST-006 observes the toggle in the running application. | ||||
| REQUIREMENT | 9 | IT-010 | Parents: | Overlay open/replace/dismiss state machine | Passed | Construct a ProviderContainer and exercise the overlay navigation subsystem through its view-models without a running UI. (a) From the initial state, call selectItem(0) on the navigation notifier and inspect the navigation and overlay state. (b) With overlay 0 open, call selectItem(1) and inspect the state. (c) With an overlay open and the bottom bar in menu mode, call clearSelection() and inspect the navigation, overlay, and bottom-bar state. |
automated | flutter_test | (a) After selectItem(0): the navigation selectedIndex is 0 and the overlay state reports an overlay visible. (b) After selectItem(1): the selectedIndex is 1 — the previous overlay is replaced, not stacked — and the overlay state still reports a single overlay visible. (c) After clearSelection(): selectedIndex is null, the overlay state reports no overlay visible, and the bottom bar has returned to signal mode. | Approved | 3fef2793b0ca54cf8bd8f7bd8454fc944d37fe9356a515a0722bf092f54b709e | @DougYoungberg | Planned test file: test/integration/overlay_navigation_integration_test.dart. The flutter_test test name carries the UID prefix (IT-010: ...) for the Flutter/JUnit join channel. Verifies the architectural integration of the overlay navigation subsystem (ARCH-010) — the NavigationNotifier / OverlayNotifier / bottom-bar coordination that enforces the single-overlay rule (SRS-010) and routes dismissal through clearSelection (SRS-009). The ESC and click-outside gestures that trigger clearSelection in the running application are verified at the system tier by ST-007. | ||||
| REQUIREMENT | 10 | IT-011 | Parents: | Bottom bar renders the per-mode controls | Passed | Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle to switch to menu mode. Inspect the rendered controls again. |
automated | flutter_test | In signal mode the bottom bar renders the six signal-parameter controls labelled HP (high-pass filter), LP (low-pass filter), SENS (sensitivity), TB (timebase), MONT (montage), and NOT (notch filter). After the toggle, in menu mode the bottom bar renders the four navigation buttons labelled Patients, Montages, Settings, and Activities. | Approved | 24e4409f0c8b2365b21455b17f6af2e095db2affca043e03f6c4e714f918957d | @DougYoungberg | Test file: test/integration/bottom_bar_integration_test.dart, in the IT-011-prefixed test case. Verifies the architectural integration of the bottom-bar mode controller (ARCH-009) rendering the per-mode content; contributes evidence to SRS-011 (signal-mode controls) and SRS-012 (menu-mode buttons). ST-006 confirms the same content at the operator-facing tier. | ||||
| REQUIREMENT | 11 | IT-012 | Parents: | Theme selection drives the application theme | Passed | Construct a ProviderContainer overriding the current-theme-type selection to each of the three theme types in turn, and read the theme-mode and theme-data providers for each. |
automated | flutter_test | For ThemeType.minimal (Dark) the theme mode is dark and the built theme has dark brightness; for ThemeType.minimalLight (Light) and ThemeType.darkGlass (Dark Glass) the theme mode is light and the built theme has light brightness. Each of the three types resolves to a theme without error. | Approved | 9562e0bfb19f555af441e01fec7921a69a881014e29539a51852e0f376e15e9b | @DougYoungberg | Test file: test/integration/theme_integration_test.dart, in the IT-012-prefixed test case. Verifies the theme subsystem (ARCH-011) maps each selectable theme type to its application theme; ST-013 confirms theme selection end-to-end at the operator-facing tier. |