package store import ( "errors" "testing" ) func TestCreateAndGetContainer(t *testing.T) { s := newTestStore(t) c, err := s.CreateContainer(Container{ WorkloadID: "wl-1", WorkloadKind: "project", Role: "prod", ContainerID: "abc123", ImageRef: "nginx:1", ImageTag: "1", State: "running", Port: 80, Subdomain: "prod-app", }) if err != nil { t.Fatalf("CreateContainer: %v", err) } if c.ID == "" { t.Fatal("container ID should be set") } if c.Host != "local" { t.Fatalf("default host should be 'local', got %q", c.Host) } got, err := s.GetContainerByID(c.ID) if err != nil { t.Fatalf("GetContainerByID: %v", err) } if got.ContainerID != "abc123" || got.Subdomain != "prod-app" { t.Fatalf("got %+v", got) } } func TestUpsertContainerInsert(t *testing.T) { s := newTestStore(t) if err := s.UpsertContainer(Container{ ID: "fixed-id", WorkloadID: "wl-1", WorkloadKind: "project", Role: "dev", State: "running", }); err != nil { t.Fatalf("UpsertContainer insert: %v", err) } got, err := s.GetContainerByID("fixed-id") if err != nil { t.Fatalf("GetContainerByID: %v", err) } if got.State != "running" { t.Fatalf("got state %q", got.State) } } func TestUpsertContainerUpdate(t *testing.T) { s := newTestStore(t) _ = s.UpsertContainer(Container{ ID: "fixed-id", WorkloadID: "wl-1", WorkloadKind: "project", Role: "dev", State: "starting", }) if err := s.UpsertContainer(Container{ ID: "fixed-id", WorkloadID: "wl-1", WorkloadKind: "project", Role: "dev", State: "running", Port: 8080, }); err != nil { t.Fatalf("UpsertContainer update: %v", err) } got, _ := s.GetContainerByID("fixed-id") if got.State != "running" || got.Port != 8080 { t.Fatalf("upsert did not update fields: %+v", got) } } func TestUpsertContainerRequiresID(t *testing.T) { s := newTestStore(t) if err := s.UpsertContainer(Container{WorkloadID: "wl-1"}); err == nil { t.Fatal("UpsertContainer without ID should fail") } } func TestGetContainerByDockerID(t *testing.T) { s := newTestStore(t) c, _ := s.CreateContainer(Container{ WorkloadID: "wl-1", WorkloadKind: "project", Role: "prod", ContainerID: "docker-xyz", State: "running", }) got, err := s.GetContainerByDockerID("docker-xyz") if err != nil { t.Fatalf("GetContainerByDockerID: %v", err) } if got.ID != c.ID { t.Fatalf("got container %s, want %s", got.ID, c.ID) } if _, err := s.GetContainerByDockerID(""); !errors.Is(err, ErrNotFound) { t.Fatalf("empty docker id should be NotFound, got %v", err) } if _, err := s.GetContainerByDockerID("ghost"); !errors.Is(err, ErrNotFound) { t.Fatalf("unknown docker id should be NotFound, got %v", err) } } func TestListContainersByWorkload(t *testing.T) { s := newTestStore(t) s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project", Role: "a"}) s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project", Role: "b"}) s.CreateContainer(Container{WorkloadID: "wl-2", WorkloadKind: "project", Role: "c"}) out, err := s.ListContainersByWorkload("wl-1") if err != nil { t.Fatalf("ListContainersByWorkload: %v", err) } if len(out) != 2 { t.Fatalf("expected 2 containers, got %d", len(out)) } } func TestListContainersWithFilter(t *testing.T) { s := newTestStore(t) s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project", State: "running"}) s.CreateContainer(Container{WorkloadID: "wl-2", WorkloadKind: "stack", State: "running"}) s.CreateContainer(Container{WorkloadID: "wl-3", WorkloadKind: "site", State: "stopped"}) out, err := s.ListContainers(ContainerFilter{WorkloadKind: "project"}) if err != nil { t.Fatalf("ListContainers kind filter: %v", err) } if len(out) != 1 || out[0].WorkloadKind != "project" { t.Fatalf("kind filter wrong: %+v", out) } out, err = s.ListContainers(ContainerFilter{State: "running"}) if err != nil { t.Fatalf("ListContainers state filter: %v", err) } if len(out) != 2 { t.Fatalf("expected 2 running, got %d", len(out)) } out, err = s.ListContainers(ContainerFilter{}) if err != nil { t.Fatalf("ListContainers no filter: %v", err) } if len(out) != 3 { t.Fatalf("expected 3 with no filter, got %d", len(out)) } } func TestListContainersByApp(t *testing.T) { s := newTestStore(t) app, _ := s.CreateApp(App{Name: "my-saas"}) w1, _ := s.CreateWorkload(Workload{Kind: "project", RefID: "p1", Name: "web", AppID: app.ID}) w2, _ := s.CreateWorkload(Workload{Kind: "stack", RefID: "s1", Name: "worker", AppID: app.ID}) wOther, _ := s.CreateWorkload(Workload{Kind: "project", RefID: "p2", Name: "other"}) s.CreateContainer(Container{WorkloadID: w1.ID, WorkloadKind: "project"}) s.CreateContainer(Container{WorkloadID: w2.ID, WorkloadKind: "stack"}) s.CreateContainer(Container{WorkloadID: wOther.ID, WorkloadKind: "project"}) out, err := s.ListContainers(ContainerFilter{AppID: app.ID}) if err != nil { t.Fatalf("ListContainers AppID: %v", err) } if len(out) != 2 { t.Fatalf("expected 2 containers in app, got %d", len(out)) } } func TestUpdateContainerState(t *testing.T) { s := newTestStore(t) c, _ := s.CreateContainer(Container{ WorkloadID: "wl-1", WorkloadKind: "project", State: "starting", }) if err := s.UpdateContainerState(c.ID, "running"); err != nil { t.Fatalf("UpdateContainerState: %v", err) } got, _ := s.GetContainerByID(c.ID) if got.State != "running" { t.Fatalf("got state %q", got.State) } if got.LastSeenAt == "" { t.Fatal("last_seen_at should have been bumped") } } func TestMarkContainerMissing(t *testing.T) { s := newTestStore(t) c, _ := s.CreateContainer(Container{ WorkloadID: "wl-1", WorkloadKind: "project", State: "running", }) if err := s.MarkContainerMissing(c.ID); err != nil { t.Fatalf("MarkContainerMissing: %v", err) } got, _ := s.GetContainerByID(c.ID) if got.State != "missing" { t.Fatalf("got state %q, want missing", got.State) } } func TestDeleteContainersByWorkload(t *testing.T) { s := newTestStore(t) s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project"}) s.CreateContainer(Container{WorkloadID: "wl-1", WorkloadKind: "project"}) s.CreateContainer(Container{WorkloadID: "wl-2", WorkloadKind: "project"}) if err := s.DeleteContainersByWorkload("wl-1"); err != nil { t.Fatalf("DeleteContainersByWorkload: %v", err) } out, _ := s.ListContainersByWorkload("wl-1") if len(out) != 0 { t.Fatalf("expected 0 after delete, got %d", len(out)) } out, _ = s.ListContainersByWorkload("wl-2") if len(out) != 1 { t.Fatalf("untouched workload should still have 1, got %d", len(out)) } }