AnnouncementsIntroducing Arkoren

    Menu

    Integration Tests

    Verify backend-specific behavior against disposable infrastructure with bounded operations and test-owned cleanup.

    What's on this page
    1. Select infrastructure tests explicitly and start a disposable, loopback-bound PostgreSQL stack.
    2. Own connection cleanup and test PostgreSQL-specific SQL with bounded operation contexts.
    3. Isolate fixtures, retain failure evidence, and remove only the test-owned stack.
    On this page

    Use a real backend when the behavior you need to verify belongs to that backend: PostgreSQL-specific SQL, Redis expiry, or broker recovery after a restart. Memory drivers and recording fakes cannot establish those semantics. Keep ordinary application-policy tests in process, and give each infrastructure test one specific backend claim.

    Select Infrastructure

    Go does not classify integration tests automatically. Put a short-mode guard before construction or connection attempts in each test that requires infrastructure:

    if testing.Short() {
    	t.Skip("integration test: requires PostgreSQL")
    }

    This fragment belongs at the start of a test function; the complete test below includes it. go test -short ./... skips guarded tests, while an ordinary run includes them. If a selected backend is unavailable, fail the test rather than silently skipping it: otherwise a broken setup can look like verified behavior.

    Start PostgreSQL

    For the example, use PostgreSQL 17 in a disposable local stack. Match the server version and important settings to your deployed backend when adapting it. Install Docker with Compose support and save this compose.yaml in your application’s module directory:

    services:
      postgres:
        image: postgres:17
        environment:
          POSTGRES_PASSWORD: local-test-only
        ports:
          - "127.0.0.1:15432:5432"
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U postgres"]
          interval: 1s
          timeout: 3s
          retries: 30

    The test password is suitable only for this loopback-bound local service. Do not use it for a shared or remotely reachable database. The health check waits for the server; the test still verifies connectivity with its actual credentials.

    From that directory, choose a new Compose project name and start the stack. Run all command blocks on this page in the same shell so the project selection remains active:

    export COMPOSE_PROJECT_NAME="reports-integration-$$"
    docker compose up -d --wait

    Use this project name only for disposable test resources. It isolates stack teardown, but not the fixed host port: simultaneous runs need separate ports and matching connection strings, or separate hosts.

    Own Connections

    Install the driver in your application’s module:

    go get arkoren.dev/driver/postgres

    In a _test.go file, import context, testing, time, and arkoren.dev/driver/postgres/postgresdatabase. This helper constructs a pool, immediately registers its cleanup, then verifies connectivity with a bounded context:

    func connectPostgres(t *testing.T) *postgresdatabase.Driver {
    	t.Helper()
    	driver, err := postgresdatabase.New(
    		"postgres://postgres:local-test-only@127.0.0.1:15432/postgres?sslmode=disable",
    	)
    	if err != nil {
    		t.Fatal(err)
    	}
    	t.Cleanup(func() {
    		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    		defer cancel()
    		if err := driver.Stop(ctx); err != nil {
    			t.Error(err)
    		}
    	})
    	ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
    	defer cancel()
    	if err := driver.Start(ctx); err != nil {
    		t.Fatal(err)
    	}
    	return driver
    }

    Construction does not prove a server connection; Start checks it. Cleanup uses its own context because t.Context() is canceled before cleanup callbacks run. Finish transactions and close cursors before stopping the pool. PostgreSQL setup owns pool configuration and native-client ownership.

    Assert Backend Behavior

    This test checks PostgreSQL JSON containment through the database facade. Add imports for arkoren.dev/core/service/database and arkoren.dev/foundation/query:

    func TestPostgresJSONContains(t *testing.T) {
    	if testing.Short() {
    		t.Skip("integration test: requires PostgreSQL")
    	}
    	db := database.New(connectPostgres(t))
    	ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
    	defer cancel()
    	statement := query.Raw("SELECT ?::jsonb @> ?::jsonb", `{"state":"ready","count":2}`, `{"state":"ready"}`)
    	contains, err := db.Find[bool](ctx, statement)
    	if err != nil {
    		t.Fatal(err)
    	}
    	if !contains {
    		t.Fatal("JSON object did not contain the expected state")
    	}
    }

    The query needs no schema or persistent fixture, and cannot be justified by a passing SQLite test. For your application’s queries, apply its migrations to a disposable database, seed the necessary rows, and call the real store operation. Driver startup does not create application tables.

    Isolate Fixtures

    Tests sharing a server need distinct resource names and ownership. Allocate a unique schema, key prefix, queue, or database for each independent fixture, including across simultaneous test runs; t.Name() alone does not distinguish separate processes. Register cleanup as soon as acquisition succeeds, and report cleanup errors with t.Error.

    Delete only resources that test created. Avoid database-wide resets or prefix scans that might match another test’s data. A transaction rolled back after a test can isolate some database work, but it cannot undo broker messages or operations performed through a different connection. Use explicit cleanup for those effects.

    Use separate fixtures for interruption, timeout, and recovery tests. State which process or connection you stop, bound waits for the expected result, and assert the application outcome after recovery. A successful reconnect alone does not prove that an interrupted write committed or a message survived.

    Run and Clean

    With the stack healthy, run the focused test without reusing a cached result:

    go test -count=1 -race -run '^TestPostgresJSONContains$' ./...

    -count=1 forces execution against the current server. A busy port or unhealthy service is a setup failure, not evidence about the driver’s semantics. If startup or testing fails, capture logs before removing the stack:

    docker compose logs --no-color

    After either success or failure, remove this disposable project and its database volumes. This deletes test data; run it only with the same test-only project selection and Compose file used above:

    docker compose down --volumes

    In CI, make teardown an always-run step and preserve the startup or test command’s failure status. Capture logs before teardown on failure. Separate fast -short tests from infrastructure jobs, and give each job its own disposable project and available ports.

    If the backend implements a custom public driver, add Driver Conformance alongside your application-specific integration tests.

    Open a bulk documentation archive?

    This file contains every guide in its scope, not API declarations. Loading it all into an agent can exhaust its context window and increase processing time and cost. For coding tasks, start with the index and read only the relevant pages.

    Use the full archive for offline indexing, bulk processing, or deliberate large-context use.

    Browse the index instead
    Open archive