Skip to content

[nightshift] test-gap: 3 packages have 0% test coverage #37

@nightshift-micr

Description

@nightshift-micr

Nightshift: test-gap analysis

Analysis of test coverage gaps in tailstick, a USB-delivered Tailscale enrollment tool with session/timed leases and autonomous cleanup.

Package coverage summary

Package Source files Test files Functions Tested Gap %
internal/model 1 (133 lines) 0 0 (types only) N/A ⚪ Type-only
internal/state 1 (82 lines) 0 4 0 🔴 100%
internal/logging 1 (63 lines) 0 4 0 🔴 100%
internal/platform 2 (216 lines) 0 9 0 🔴 100%
internal/app 3 (999 lines) 1 (533 lines) ~25 ~15 🟡 ~40%
internal/config 1 (116 lines) 1 (38 lines) 3 1 🟡 ~33%
internal/crypto 1 (128 lines) 1 (33 lines) ~5 ~2 🟡 ~60%
internal/gui 1 (224 lines) 1 (132 lines) ~8 2 🟡 ~25%
internal/tailscale 1 (270 lines) 2 (145 lines) ~10 ~5 🟡 ~50%

Critical gaps (🔴 0% coverage)

internal/state — No tests at all

Severity: HIGH — This is the persistent state layer, the source of truth for lease lifecycle.

  • Load() — Reads/parses JSON state; returns empty state for missing files. Untested: corrupt JSON handling, schema version defaulting.
  • Save() — Atomic write with tmp file. Untested: directory creation, permission bits (0600), overwrite behavior.
  • UpsertRecord() — Core upsert logic. Untested: insert new record, update existing record by LeaseID.
  • AppendAudit() — Append-only audit log. Untested: directory creation, JSONL format, concurrent writes.

internal/logging — No tests at all

Severity: MEDIUM — Logging is infrastructure, not business logic.

  • New() — Untested: directory creation, file open behavior.
  • Close() — Untested: double-close safety, nil file handling.
  • write() — Untested: output format (RFC3339 + level + message), mutex behavior.
  • Info() / Error() — Untested: level formatting.

internal/platform — No tests at all

Severity: HIGH — Platform detection affects every operation.

  • Detect() — Untested on Linux (boot_id read, hostname sanitization).
  • sanitizeHost() — Untested: special chars, empty input, Unicode, leading/trailing hyphens.
  • IsElevated() — Untested: Linux euid check.
  • StatePath() / LogPath() / LocalSecretPath() / AgentBinaryPath() — Untested: Windows vs Linux path selection.
  • RequireSupportedLinux() — Untested: Ubuntu/Debian detection, unsupported distro rejection.
  • Runner.Run() — Untested: dry-run mode, empty command, context cancellation.
  • Runner.RunWithTimeout() — Untested: timeout propagation.

Moderate gaps (🟡 partial coverage)

internal/config — 1 test, 2 functions untested

Severity: MEDIUM

  • Only Load() with env-var resolution is tested.
  • Missing: Load() with missing file, corrupt JSON, authKeyEnv/ephemeralAuthKeyEnv resolution, stableVersionOverride defaults.
  • Missing: resolveEnvVars() edge cases (empty env, nested env references).

internal/crypto — 2 tests, partial coverage

Severity: MEDIUM

  • Encrypt() / Decrypt() round-trip is likely tested.
  • Missing: key derivation edge cases, empty plaintext, large payloads, wrong-key decryption failure.

internal/gui — 2 endpoint tests, ~6 endpoints untested

Severity: MEDIUM

  • TestPresetsRedactsSecretsAndOnlyAllowsGet — presets endpoint.
  • TestEnrollRejectsInvalidModeAndNegativeDurations — enroll validation.
  • Missing: DELETE /api/leases/{id}, GET /api/status, GET /api/leases, WebSocket/SSE endpoints (if any), CORS behavior.

internal/tailscale — ~50% coverage

Severity: LOW-MEDIUM

  • client_test.go tests some API interactions.
  • context_test.go is minimal (11 lines).
  • Missing: error path coverage (network timeouts, malformed JSON from tailscale status), auth key validation.

internal/app — ~40% coverage (workflow_test.go is the largest test file)

Severity: LOW-MEDIUM

  • workflow_test.go (533 lines) covers the core workflow engine.
  • Missing: cli.go command parsing tests (196 lines untested), gui.go integration tests.

Recommended test priorities

  1. P0 — internal/state: Test UpsertRecord(), Load() (missing file + corrupt JSON), Save() (atomic write). Pure functions, easy to test with t.TempDir().
  2. P0 — internal/platform: Test sanitizeHost(), Runner.Run() (dry-run mode), StatePath()/LogPath() paths. Most are pure functions.
  3. P1 — internal/config: Test env resolution for all credential fields, missing file behavior.
  4. P1 — internal/gui: Test remaining API endpoints, especially DELETE /api/leases/{id}.
  5. P2 — internal/logging: Test output format and Close() safety.
  6. P2 — internal/crypto: Edge case coverage.
  7. P3 — internal/tailscale: Error path coverage.
  8. P3 — internal/app/cli.go: Command flag parsing.

Testing patterns observed

  • Uses stdlib testing package (no testify or ginkgo).
  • Uses t.TempDir() for filesystem tests.
  • Uses httptest.NewRequest / httptest.NewRecorder for HTTP tests.
  • Good pattern: table-driven tests with named sub-tests (see gui/server_test.go).
  • Good pattern: function injection for testability (see Server.EnrollFn).

Estimated effort

Package Tests to add Estimated LOC Difficulty
state 6-8 120-160 Easy
platform 8-10 150-200 Easy
config 3-4 60-80 Easy
logging 3-4 60-80 Easy
gui 4-6 100-150 Medium
Total 24-32 490-670

Generated by nightshift — test-gap task.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions