BioFlow Requirements
Integration Test Specifications

Integration Test Specifications

UID: DOC-IT
Type LevelMIDUIDREFS TitleResult Statement Rationale CommentTEST_METHODTEST_TOOLACCEPTANCE_CRITERIASTATUSREVIEWED_HASHREVIEWED_BYQMS_REVIEW_DATENOTES
REQUIREMENT 1 IT-001
IT-001
change in progress
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
IT-002
change in progress
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
IT-003
change in progress
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
IT-004
change in progress
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
IT-005
change in progress
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
IT-006
change in progress
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
IT-008
reviewed
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
IT-009
reviewed
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
IT-010
reviewed
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
IT-011
reviewed
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
IT-012
reviewed
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.