Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

18KB


title: ‘ExportInkjetFile Integration Test’ slug: ‘exportinkjetfile-integration-test’ created: ‘2026-03-17’ status: ‘Implementation Complete’ stepsCompleted: [1, 2, 3, 4] tech_stack: [‘VBScript’, ‘ADODB’, ‘Chilkat_9_5_0.Csv’, ‘Access MDB/Jet’] files_to_modify: [‘Tests/TrackingDataImport_TestHarness.vbs’] code_patterns: [‘LoadFunctions+ExecuteGlobal isolation’, ‘ADODB connection to MDB’, ‘Chilkat CSV read-back’] test_patterns: [‘Integration test with real MDB fixture’, ‘Assert file output + DB side effect’]

Tech-Spec: ExportInkjetFile Integration Test

Created: 2026-03-17

Overview

Problem Statement

ExportInkjetFile(KitID) produces the inkjet operator CSV used on press day. It has zero automated coverage. The function joins InkjetRecords + KitLabels + Colors from an Access MDB, builds a Chilkat CSV with 22 columns, writes it to disk, then marks Kit.Status='Done’. A regression here causes silent bad output with no test catching it.

Solution

Add an ADODB integration test directly to Tests/TrackingDataImport_TestHarness.vbs. The harness opens Data/webdata - Copy.mdb via ADODB, calls ExportInkjetFile on a real KitID, and asserts CSV structure (file exists, 22 columns, correct headers) plus ~10 row-level field values and the DB side-effect (Kit.Status='Done’, InkJetJob=1).

Scope

In Scope:

  • Set up integration test globals in harness: oConn, ConnectionString (pointing to Data/webdata - Copy.mdb), ExportDirectory (temp path under Tests/)
  • Load GetSetting and ExportInkjetFile into the functionNames array
  • Query MDB to discover a real KitID that has InkjetRecords
  • Call ExportInkjetFile(KitID)
  • Assert: CSV file exists at expected path
  • Assert: 22 column headers correct (spot-check cols 0, 5, 8, 11, 19, 20, 21)
  • Assert: CSV row count = InkjetRecords count for that KitID
  • Assert: First 10 rows — “Ballot Number” has leading zeros stripped, “Matching Code” starts with JCode
  • Assert: Kit.Status = ‘Done’ and Kit.InkJetJob = 1 after call
  • Cleanup: delete temp export folder

Out of Scope:

  • Pure logic extraction from ExportInkjetFile (not viable — all field assembly is inline with recordset reads; refactoring would be required)
  • Seeding fixture data (real kit + inkjet records already exist in Data/webdata - Copy.mdb)
  • Office copies rows (separate conditional path; can be added later if a Kit with OfficeCopiesAmount > 0 is present)
  • Full row-by-row validation (10-record spot check is sufficient)
  • Restoring Kit.Status after test (MDB copy is disposable; original webdata.mdb is untouched)

Context for Development

Codebase Patterns

  • Harness pattern: LoadFunctions(filePath, functionNames) extracts named functions from source via text parsing; ExecuteGlobal makes them available in harness global scope. Global vars set before calling functions are visible inside loaded functions.
  • LoadFunctions is called at line 34, before any test code. Adding "GetSetting" and "ExportInkjetFile" to the functionNames array (lines 16–32) is sufficient — they'll be extracted and available globally.
  • Set objFSO = fso at line 36 — already set. ExportInkjetFile uses objFSO internally; this is already satisfied.
  • chilkatAvailable declared at line 39 — already exists. New integrationDbAvailable follows the same declare-in-preamble, set-in-init-block pattern.
  • Assertion API: The harness has no Assert sub — only AssertEqual(actual, expected, label) and AssertArrayEqual. All integration test assertions must use AssertEqual condition, True, "label" form.
  • DB dependency chain: ExportInkjetFile calls oConn.Open(ConnectionString) if oConn.State = 0. It manages its own connection lifecycle. The harness must set oConn (via Set oConn = CreateObject("ADODB.Connection")) and ConnectionString as globals before calling.
  • ExportDirectory global: Used by ExportInkjetFile as-is (no trailing slash added internally). Must include trailing \ in the harness assignment.
  • Chilkat CSV write-back verification: ExportInkjetFile creates its own internal Chilkat_9_5_0.Csv object. Chilkat unlock is process-wide — already done in harness init block. Read-back uses a separate object.

Files to Reference

File Purpose
Tests/TrackingDataImport_TestHarness.vbs Target file — 3 insertion points: globals (after line 15), functionNames array (after line 31), init block (after line 69), test block (before line 206)
ImportService/TrackingDataImport.vbs Source — ExportInkjetFile at line 251 (Function), GetSetting also in same file
Data/webdata - Copy.mdb MDB fixture — real Kit + InkjetRecords + KitLabels + Colors + Jurisdiction + Contacts + Settings

Technical Decisions

  • Use Data/webdata - Copy.mdb directly: It's already a copy and contains real data. No need to seed a new fixture. The UPDATE side effect (Kit.Status='Done’) is acceptable since this MDB is a disposable copy.
  • Discover KitID at runtime via JOIN: Query SELECT TOP 1 ir.KitID FROM ((InkjetRecords ir INNER JOIN Kit k ON ir.KitID = k.ID) INNER JOIN Jurisdiction j ON k.JCode = j.JCode) INNER JOIN Contacts c ON k.JCode = c.JURISCODE to ensure discovered KitID has all required related records. Avoid orphan-crash on JurisdictionRs or ContactRs inside the function.
  • ExportDirectory as temp path: Set to fso.BuildPath(scriptDir, "export-test-output"). Delete before call (not just after) to ensure no stale CSV from a prior failed run contaminates assertions.
  • ADODB driver probe: Try Microsoft.ACE.OLEDB.12.0 first; fall back to Microsoft.Jet.OLEDB.4.0. If both fail, skip with message. Guard with fso.FileExists(integrationMdbPath) before attempting open.
  • Load both GetSetting and ExportInkjetFile in functionNames array — GetSetting is a DB-backed dependency called internally by ExportInkjetFile.
  • Chilkat read-back: Capture verifyCsv.LoadFile(csvPath) return value and assert it before proceeding to column/row assertions — prevents vacuously-passing assertions on a failed load.
  • Cleanup is error-suppressed: Wrap fso.DeleteFolder in On Error Resume Next / On Error GoTo 0 — AV scanners may briefly hold a handle on the new CSV; a cleanup failure should not fail the test.

Implementation Plan

Tasks

  • Task 1: Add integration test globals and ADODB probe block
    • File: Tests/TrackingDataImport_TestHarness.vbs
    • Action: Insert 7 Dim declarations after line 15; insert ADODB probe block after line 69
    • Notes: Sets integrationDbAvailable/integrationDbSkipReason — same guard pattern as chilkatAvailable

Insertion point: after line 15 (Dim DataDirectory). Add:

Dim oConn
Dim ConnectionString
Dim ExportDirectory
Dim integrationMdbPath
Dim integrationExportDir
Dim integrationDbAvailable
Dim integrationDbSkipReason

Insertion point: after line 69 (On Error GoTo 0 at end of Chilkat init block). Add ADODB probe block:

integrationMdbPath = fso.BuildPath(scriptDir, "..\Data\webdata - Copy.mdb")
integrationExportDir = fso.BuildPath(scriptDir, "export-test-output")
integrationDbAvailable = False
integrationDbSkipReason = ""
If Not fso.FileExists(integrationMdbPath) Then
    integrationDbSkipReason = "MDB fixture not found: " & integrationMdbPath
Else
    Set oConn = CreateObject("ADODB.Connection")
    On Error Resume Next
    oConn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & integrationMdbPath & ";"
    If Err.Number <> 0 Then
        Err.Clear
        oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & integrationMdbPath & ";"
    End If
    If Err.Number <> 0 Then
        integrationDbSkipReason = "No ADODB provider available (ACE and Jet both failed): " & Err.Description
        Err.Clear
    Else
        integrationDbAvailable = True
        ConnectionString = oConn.ConnectionString
        ExportDirectory = integrationExportDir & "\"
    End If
    On Error GoTo 0
    If oConn.State = 1 Then oConn.Close
End If
  • Task 2: Add GetSetting and ExportInkjetFile to functionNames array
    • File: Tests/TrackingDataImport_TestHarness.vbs
    • Action: Extend array at lines 30–31 to include two new entries
    • Notes: Must be done before LoadFunctions call at line 34; order within array does not matter

Insertion point: lines 30–31. Change:

    "CheckStringDoesNotHaveForiegnCountries", _
    "ValidImportCSV" _

To:

    "CheckStringDoesNotHaveForiegnCountries", _
    "ValidImportCSV", _
    "GetSetting", _
    "ExportInkjetFile" _
  • Task 3: Add ExportInkjetFile integration test block
    • File: Tests/TrackingDataImport_TestHarness.vbs
    • Action: Insert full integration test block before line 206 (WScript.Echo "")
    • Notes: Guarded by chilkatAvailable AND integrationDbAvailable; uses AssertEqual throughout (no Assert sub exists); pre-call dir cleanup, post-call DB side-effect check, error-suppressed cleanup

Insertion point: before line 206 (WScript.Echo ""). Add:

' === ExportInkjetFile Integration Test ===
If Not chilkatAvailable Then
    WScript.Echo "SKIP: ExportInkjetFile integration test (Chilkat unavailable)"
ElseIf Not integrationDbAvailable Then
    WScript.Echo "SKIP: ExportInkjetFile integration test (" & integrationDbSkipReason & ")"
Else
    oConn.Open ConnectionString

    Dim kitDiscoverRs
    Set kitDiscoverRs = oConn.Execute( _
        "SELECT TOP 1 ir.KitID FROM ((InkjetRecords ir " & _
        "INNER JOIN Kit k ON ir.KitID = k.ID) " & _
        "INNER JOIN Jurisdiction j ON k.JCode = j.JCode) " & _
        "INNER JOIN Contacts c ON k.JCode = c.JURISCODE;")
    If kitDiscoverRs.EOF Then
        WScript.Echo "SKIP: ExportInkjetFile — no complete Kit+InkjetRecords+Jurisdiction+Contact found in fixture MDB"
        oConn.Close
    Else
        Dim testKitID : testKitID = kitDiscoverRs("KitID").Value

        Dim countRs
        Set countRs = oConn.Execute("SELECT COUNT(*) AS N FROM InkjetRecords WHERE KitID=" & testKitID & ";")
        Dim expectedRows : expectedRows = countRs("N").Value

        Dim kitRsCheck
        Set kitRsCheck = oConn.Execute("SELECT JCode FROM Kit WHERE ID=" & testKitID & ";")
        Dim testJCode : testJCode = kitRsCheck("JCode").Value

        oConn.Close

        ' Delete stale output before call, then recreate
        On Error Resume Next
        If fso.FolderExists(integrationExportDir) Then fso.DeleteFolder(integrationExportDir, True)
        On Error GoTo 0
        fso.CreateFolder integrationExportDir

        ExportInkjetFile testKitID

        Dim exportSubfolders : Set exportSubfolders = fso.GetFolder(integrationExportDir).SubFolders
        Dim csvFound : csvFound = False
        Dim csvPath : csvPath = ""
        Dim sf
        For Each sf In exportSubfolders
            Dim csvFiles : Set csvFiles = sf.Files
            Dim f
            For Each f In csvFiles
                If LCase(fso.GetExtensionName(f.Name)) = "csv" Then
                    csvFound = True
                    csvPath = f.Path
                End If
            Next
        Next

        AssertEqual csvFound, True, "[INT] ExportInkjetFile: CSV file created"

        If csvFound Then
            Dim verifyCsv : Set verifyCsv = CreateObject("Chilkat_9_5_0.Csv")
            verifyCsv.HasColumnNames = 1
            Dim csvLoaded : csvLoaded = verifyCsv.LoadFile(csvPath)
            AssertEqual csvLoaded, True, "[INT] ExportInkjetFile: CSV loaded by Chilkat"

            If csvLoaded Then
                AssertEqual verifyCsv.NumColumns, 22, "[INT] ExportInkjetFile: 22 columns"
                AssertEqual verifyCsv.ColumnName(0), "Full Name", "[INT] ExportInkjetFile: col 0 = Full Name"
                AssertEqual verifyCsv.ColumnName(5), "IM barcode Characters", "[INT] ExportInkjetFile: col 5 = IM barcode Characters"
                AssertEqual verifyCsv.ColumnName(8), "Ballot Number", "[INT] ExportInkjetFile: col 8 = Ballot Number"
                AssertEqual verifyCsv.ColumnName(11), "Combined  Pct_Ballot Num", "[INT] ExportInkjetFile: col 11 = Combined  Pct_Ballot Num"
                AssertEqual verifyCsv.ColumnName(19), "Matching Code", "[INT] ExportInkjetFile: col 19 = Matching Code"
                AssertEqual verifyCsv.ColumnName(20), "ColorFilepath", "[INT] ExportInkjetFile: col 20 = ColorFilepath"
                AssertEqual verifyCsv.ColumnName(21), "ColorName", "[INT] ExportInkjetFile: col 21 = ColorName"
                AssertEqual verifyCsv.NumRows, expectedRows, "[INT] ExportInkjetFile: row count matches InkjetRecords"

                Dim checkRows : checkRows = 10
                If verifyCsv.NumRows < 10 Then checkRows = verifyCsv.NumRows
                Dim r
                For r = 0 To checkRows - 1
                    Dim ballotNum : ballotNum = verifyCsv.GetCell(r, 8)
                    AssertEqual (Left(ballotNum, 1) <> "0" Or ballotNum = ""), True, "[INT] ExportInkjetFile: row " & r & " Ballot Number no leading zeros"
                    Dim matchCode : matchCode = verifyCsv.GetCell(r, 19)
                    AssertEqual Left(matchCode, Len(testJCode)), testJCode, "[INT] ExportInkjetFile: row " & r & " Matching Code starts with JCode"
                Next
            End If

            Set verifyCsv = Nothing
        End If

        oConn.Open ConnectionString
        Dim sideEffectRs
        Set sideEffectRs = oConn.Execute("SELECT Status, InkJetJob FROM Kit WHERE ID=" & testKitID & ";")
        AssertEqual sideEffectRs("Status").Value, "Done", "[INT] ExportInkjetFile: Kit.Status = Done"
        AssertEqual sideEffectRs("InkJetJob").Value, 1, "[INT] ExportInkjetFile: Kit.InkJetJob = 1"
        oConn.Close

        On Error Resume Next
        If fso.FolderExists(integrationExportDir) Then fso.DeleteFolder(integrationExportDir, True)
        On Error GoTo 0
    End If
End If
  • Task 4: Verify Option Explicit compliance
    • File: Tests/TrackingDataImport_TestHarness.vbs
    • Action: Review all new variable names introduced in Tasks 1–3 and confirm each has a Dim declaration
    • Notes: New inline Dims in Task 3: kitDiscoverRs, testKitID, countRs, expectedRows, kitRsCheck, testJCode, exportSubfolders, csvFound, csvPath, sf, csvFiles, f, verifyCsv, csvLoaded, checkRows, r, ballotNum, matchCode, sideEffectRs — all must be present

Acceptance Criteria

  • AC 1: Given Chilkat is available and Data/webdata - Copy.mdb contains a Kit with InkjetRecords, Jurisdiction, and Contact, when the harness calls ExportInkjetFile(testKitID), then a CSV file is created under Tests/export-test-output/<JobNumber>-<Name>/, it has exactly 22 columns with correct header names at indices 0/5/8/11/19/20/21, row count matches the InkjetRecords count, the first 10 rows have no leading zeros in “Ballot Number”, the first 10 rows have “Matching Code” starting with JCode, and Kit.Status='Done’ + Kit.InkJetJob=1 in the MDB.

  • AC 2: Given Chilkat is unavailable, when the harness reaches the ExportInkjetFile block, then it prints SKIP: ExportInkjetFile integration test (Chilkat unavailable) and no test failures are recorded.

  • AC 3: Given Data/webdata - Copy.mdb is absent or neither ACE nor Jet ADODB driver is available, when the ADODB probe block runs during harness init, then integrationDbAvailable is False, the test block prints a SKIP message with the reason, and no test failures are recorded.

  • AC 4: Given the MDB contains no Kit with all required related records (InkjetRecords + Jurisdiction + Contacts), when the discovery JOIN query runs, then it prints SKIP: ExportInkjetFile — no complete Kit+InkjetRecords+Jurisdiction+Contact found in fixture MDB and no test failures are recorded.

  • AC 5: Given a prior test run left Tests/export-test-output/ behind (simulating a failed cleanup), when the integration test runs, then the stale folder is deleted before ExportInkjetFile is called and the test proceeds without contaminated assertions.


Additional Context

Dependencies

  • Chilkat_9_5_0.Csv COM — must be registered and unlocked (guarded by chilkatAvailable)
  • Microsoft.ACE.OLEDB.12.0 or Microsoft.Jet.OLEDB.4.0 — must be available for ADODB to open the MDB
  • Data/webdata - Copy.mdb — must exist with real Kit + InkjetRecords + KitLabels + Colors + Jurisdiction + Contacts + Settings(ElectionDate)
  • ADODB.Connection — available on any Windows machine with Office/Access drivers

Testing Strategy

Integration test only — no unit-level testing is viable for this function without significant refactoring. The test confirms the full output pipeline from DB read → CSV write → DB update.

Notes

  • The ExportInkjetFile function uses objFSO (not fso). The line Set objFSO = fso was added in a prior workflow pass and is already present in the harness.
  • Column index 11 has a double-space in its name: "Combined Pct_Ballot Num" — this is as-written in the source (line 296).
  • The Chilkat CSV column indices: source sets cols 0–21 (22 columns total). The ColumnName(n) method on the read-back CSV object uses 0-based index.
  • ADODB driver probe: Init block tries ACE first (Microsoft.ACE.OLEDB.12.0), falls back to Jet (Microsoft.Jet.OLEDB.4.0). Bitness matters — 32-bit cscript requires 32-bit drivers. Sets integrationDbAvailable = True/False and integrationDbSkipReason.
  • oConn lifecycle: The probe block opens and immediately closes oConn to validate the connection string. ExportInkjetFile internally calls oConn.Open(ConnectionString) if State=0 — so close it before calling the function. Reopen after for the side-effect assertion.
  • KitID discovery JOIN: Uses 4-table JOIN (InkjetRecords + Kit + Jurisdiction + Contacts) to guarantee no orphan-crash inside ExportInkjetFile when it accesses JurisdictionRs("Name") or ContactRs("Title").
  • Pre-call cleanup: integrationExportDir is deleted before calling ExportInkjetFile (not just after) to prevent stale CSVs from prior failed runs contaminating assertions.

Powered by TurnKey Linux.