package sqlite import ( "context" "database/sql" "errors" "strings" "testing" ) func TestPacksRepoCreateAndGet(t *testing.T) { store := openTestDB(t) id, err := store.Packs().Create(context.Background(), Pack{ PackID: "test-pack", Version: "1.0.0", Checksum: "abc123", Vendor: "test-vendor", TargetHost: "sub2api", MinHostVersion: "0.1.0", MaxHostVersion: "0.2.x", ManifestJSON: `{"name":"test"}`, }) if err != nil { t.Fatalf("Create() error = %v", err) } if id <= 0 { t.Fatalf("Create() id = %d, want positive", id) } got, err := store.Packs().GetByID(context.Background(), id) if err != nil { t.Fatalf("GetByID() error = %v", err) } if got.PackID != "test-pack" || got.Version != "1.0.0" { t.Fatalf("GetByID() = %+v, want pack test-pack", got) } got2, err := store.Packs().GetByPackID(context.Background(), "test-pack") if err != nil { t.Fatalf("GetByPackID() error = %v", err) } if got2.ID != id { t.Fatalf("GetByPackID() id = %d, want %d", got2.ID, id) } } func TestPacksRepoCreateDefaultsManifestJSON(t *testing.T) { store := openTestDB(t) id, err := store.Packs().Create(context.Background(), Pack{ PackID: "no-manifest", Version: "1.0.0", Checksum: "chk", }) if err != nil { t.Fatalf("Create() error = %v", err) } got, err := store.Packs().GetByID(context.Background(), id) if err != nil { t.Fatalf("GetByID() error = %v", err) } if got.ManifestJSON != "{}" { t.Fatalf("ManifestJSON = %q, want {}", got.ManifestJSON) } } func TestPacksRepoUpsertCreatesNew(t *testing.T) { store := openTestDB(t) id, err := store.Packs().Upsert(context.Background(), Pack{ PackID: "upsert-pack", Version: "1.0.0", Checksum: "chk1", }) if err != nil { t.Fatalf("Upsert() error = %v", err) } if id <= 0 { t.Fatalf("Upsert() id = %d, want positive", id) } } func TestPacksRepoUpsertUpdatesExisting(t *testing.T) { store := openTestDB(t) id1, err := store.Packs().Upsert(context.Background(), Pack{ PackID: "upsert-pack", Version: "1.0.0", Checksum: "chk1", }) if err != nil { t.Fatalf("Upsert() create error = %v", err) } id2, err := store.Packs().Upsert(context.Background(), Pack{ PackID: "upsert-pack", Version: "2.0.0", Checksum: "chk2", }) if err != nil { t.Fatalf("Upsert() update error = %v", err) } if id2 != id1 { t.Fatalf("Upsert() update returned id %d, want original %d", id2, id1) } got, err := store.Packs().GetByID(context.Background(), id1) if err != nil { t.Fatalf("GetByID() error = %v", err) } if got.Version != "2.0.0" { t.Fatalf("Version after upsert = %q, want 2.0.0", got.Version) } } func TestPacksRepoUpsertTrimsAndDefaultsManifest(t *testing.T) { store := openTestDB(t) id, err := store.Packs().Upsert(context.Background(), Pack{ PackID: " upsert-pack-json ", Version: " 1.2.3 ", Checksum: " chk-json ", Vendor: " vendor-a ", TargetHost: " sub2api ", MinHostVersion: " 0.1.0 ", MaxHostVersion: " 0.2.x ", }) if err != nil { t.Fatalf("Upsert() error = %v", err) } got, err := store.Packs().GetByID(context.Background(), id) if err != nil { t.Fatalf("GetByID() error = %v", err) } if got.PackID != "upsert-pack-json" { t.Fatalf("PackID = %q, want upsert-pack-json", got.PackID) } if got.Version != "1.2.3" { t.Fatalf("Version = %q, want 1.2.3", got.Version) } if got.Checksum != "chk-json" { t.Fatalf("Checksum = %q, want chk-json", got.Checksum) } if got.Vendor != "vendor-a" { t.Fatalf("Vendor = %q, want vendor-a", got.Vendor) } if got.TargetHost != "sub2api" { t.Fatalf("TargetHost = %q, want sub2api", got.TargetHost) } if got.MinHostVersion != "0.1.0" { t.Fatalf("MinHostVersion = %q, want 0.1.0", got.MinHostVersion) } if got.MaxHostVersion != "0.2.x" { t.Fatalf("MaxHostVersion = %q, want 0.2.x", got.MaxHostVersion) } if got.ManifestJSON != "{}" { t.Fatalf("ManifestJSON = %q, want {}", got.ManifestJSON) } } func TestPacksRepoUpsertFallsBackWhenLastInsertIDUnavailable(t *testing.T) { store := openTestDB(t) ctx := context.Background() db := store.SQLDB() repo := newPacksRepo(execQuerierStub{ execFn: func(ctx context.Context, query string, args ...any) (sql.Result, error) { if _, err := db.ExecContext(ctx, query, args...); err != nil { return nil, err } return resultStub{lastInsertErr: errors.New("last insert unavailable")}, nil }, queryFn: db.QueryContext, queryRowFn: db.QueryRowContext, }) id, err := repo.Upsert(ctx, Pack{ PackID: "fallback-pack", Version: "1.0.0", Checksum: "fallback-checksum", }) if err != nil { t.Fatalf("Upsert() error = %v", err) } if id <= 0 { t.Fatalf("Upsert() id = %d, want positive", id) } got, err := store.Packs().GetByPackID(ctx, "fallback-pack") if err != nil { t.Fatalf("GetByPackID() error = %v", err) } if got.ID != id { t.Fatalf("fallback id = %d, want persisted id %d", id, got.ID) } } func TestPacksRepoUpsertReturnsExecError(t *testing.T) { ctx := context.Background() repo := newPacksRepo(execQuerierStub{ execFn: func(context.Context, string, ...any) (sql.Result, error) { return nil, errors.New("exec boom") }, queryFn: func(context.Context, string, ...any) (*sql.Rows, error) { return nil, errors.New("unexpected query") }, queryRowFn: func(context.Context, string, ...any) *sql.Row { panic("unexpected QueryRowContext") }, }) _, err := repo.Upsert(ctx, Pack{ PackID: "pack-exec-error", Version: "1.0.0", Checksum: "checksum", }) if err == nil || !strings.Contains(err.Error(), "exec boom") { t.Fatalf("Upsert() error = %v, want exec boom", err) } } func TestPacksRepoUpsertReturnsFallbackReadError(t *testing.T) { store := openTestDB(t) ctx := context.Background() db := store.SQLDB() repo := newPacksRepo(execQuerierStub{ execFn: func(context.Context, string, ...any) (sql.Result, error) { return resultStub{lastInsertErr: errors.New("last insert unavailable")}, nil }, queryFn: db.QueryContext, queryRowFn: db.QueryRowContext, }) _, err := repo.Upsert(ctx, Pack{ PackID: "pack-missing-after-upsert", Version: "1.0.0", Checksum: "checksum", }) if !errors.Is(err, sql.ErrNoRows) { t.Fatalf("Upsert() error = %v, want sql.ErrNoRows fallback read error", err) } } func TestPacksRepoValidationErrors(t *testing.T) { store := openTestDB(t) tests := []struct { name string pack Pack }{ {"empty pack_id", Pack{Version: "v", Checksum: "c"}}, {"empty version", Pack{PackID: "p", Checksum: "c"}}, {"empty checksum", Pack{PackID: "p", Version: "v"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := store.Packs().Create(context.Background(), tt.pack) if err == nil { t.Fatal("Create() error = nil, want validation error") } }) } } func TestPacksRepoUpsertValidationErrors(t *testing.T) { store := openTestDB(t) tests := []struct { name string pack Pack }{ {"empty pack_id", Pack{Version: "v", Checksum: "c"}}, {"empty version", Pack{PackID: "p", Checksum: "c"}}, {"empty checksum", Pack{PackID: "p", Version: "v"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := store.Packs().Upsert(context.Background(), tt.pack) if err == nil { t.Fatal("Upsert() error = nil, want validation error") } }) } } func TestPacksRepoGetByIDNotFound(t *testing.T) { store := openTestDB(t) _, err := store.Packs().GetByID(context.Background(), 999) if !errors.Is(err, sql.ErrNoRows) { t.Fatalf("GetByID(999) error = %v, want sql.ErrNoRows", err) } } func TestPacksRepoGetByPackIDEmptyError(t *testing.T) { store := openTestDB(t) _, err := store.Packs().GetByPackID(context.Background(), "") if err == nil { t.Fatal("GetByPackID('') error = nil, want error") } } func TestPacksRepoGetByPackIDNotFound(t *testing.T) { store := openTestDB(t) _, err := store.Packs().GetByPackID(context.Background(), "nonexistent") if !errors.Is(err, sql.ErrNoRows) { t.Fatalf("GetByPackID() error = %v, want sql.ErrNoRows", err) } } func TestPacksRepoListAll(t *testing.T) { store := openTestDB(t) packs, err := store.Packs().ListAll(context.Background()) if err != nil { t.Fatalf("ListAll() empty error = %v", err) } if len(packs) != 0 { t.Fatalf("ListAll() empty len = %d, want 0", len(packs)) } if _, err := store.Packs().Create(context.Background(), Pack{PackID: "pack-a", Version: "1.0.0", Checksum: "chk-a"}); err != nil { t.Fatalf("Create(pack-a) error = %v", err) } if _, err := store.Packs().Create(context.Background(), Pack{PackID: "pack-b", Version: "1.1.0", Checksum: "chk-b"}); err != nil { t.Fatalf("Create(pack-b) error = %v", err) } packs, err = store.Packs().ListAll(context.Background()) if err != nil { t.Fatalf("ListAll() error = %v", err) } if len(packs) != 2 { t.Fatalf("ListAll() len = %d, want 2", len(packs)) } if packs[0].PackID != "pack-a" || packs[1].PackID != "pack-b" { t.Fatalf("ListAll() ids = [%q %q], want [pack-a pack-b]", packs[0].PackID, packs[1].PackID) } }