package compose import ( "context" "testing" ) func TestParseLabels(t *testing.T) { got := parseLabels("com.docker.compose.project=rsmon,com.docker.compose.service=web,foo=a=b") if got[LabelComposeProject] != "rsmon" { t.Fatalf("project = %q", got[LabelComposeProject]) } if got[LabelComposeService] != "web" { t.Fatalf("service = %q", got[LabelComposeService]) } if got["foo"] != "a=b" { // value may contain '=' t.Fatalf("foo = %q", got["foo"]) } if len(parseLabels("")) != 0 { t.Fatal("empty labels should yield empty map") } } func TestGroupContainers(t *testing.T) { res := &DiscoveryResult{Projects: map[string]Project{ "rsmon": {Name: "rsmon", Status: "running(2)", ConfigFiles: "/compose/rsmon/docker-compose.yml", Services: map[string]Service{}}, }} containers := []Container{ { ID: "a", Name: "rsmon-web", Image: "nginx", State: "running", Labels: map[string]string{LabelComposeProject: "rsmon", LabelComposeService: "web", LabelComposeWorkingDir: "/compose/rsmon", LabelComposeConfigFiles: "/compose/rsmon/docker-compose.yml"}, }, { ID: "b", Name: "rsmon-backend", Image: "rsmon", State: "running", Labels: map[string]string{LabelComposeProject: "rsmon", LabelComposeService: "backend"}, }, { ID: "c", Name: "loose", State: "running", Labels: map[string]string{}, // not a compose container }, } groupContainers(res, containers) proj, ok := res.Projects["rsmon"] if !ok { t.Fatal("rsmon project missing") } if proj.WorkingDir != "/compose/rsmon" { t.Fatalf("working dir = %q", proj.WorkingDir) } if len(proj.Services) != 2 { t.Fatalf("service count = %d", len(proj.Services)) } if got := proj.Services["web"].Containers[0].Service; got != "web" { t.Fatalf("web service tag = %q", got) } if _, ok := res.Projects["loose"]; ok { t.Fatal("non-compose container should not create a project") } } func TestParseTraefikLabels(t *testing.T) { routes := ParseTraefikLabels(map[string]string{ "traefik.http.routers.rsmon.rule": "Host(`rsmon.rscx.ru`)", "traefik.http.routers.rsmon-api.rule": "PathPrefix(`/api`)", "traefik.http.routers.rsmon.service": "rsmon", // non-rule label, ignored "traefik.http.middlewares.redirect.rule": "not a router", // wrong prefix path }) if len(routes) != 2 { t.Fatalf("route count = %d", len(routes)) } var hosts, prefixes []string for _, r := range routes { hosts = append(hosts, r.Hostnames...) prefixes = append(prefixes, r.PathPrefixes...) } if !contains(hosts, "rsmon.rscx.ru") { t.Fatalf("hosts = %v", hosts) } if !contains(prefixes, "/api") { t.Fatalf("prefixes = %v", prefixes) } } func contains(s []string, v string) bool { for _, x := range s { if x == v { return true } } return false } func TestBuildProjectSummary(t *testing.T) { res := &DiscoveryResult{Projects: map[string]Project{ "p": { Name: "p", Services: map[string]Service{ "web": {Name: "web", Containers: []Container{ {Name: "web-1", State: "running", Image: "nginx", Ports: []PortBinding{{HostPort: "80"}}}, }}, "db": {Name: "db", Containers: []Container{ {Name: "db-1", State: "exited", Image: "postgres"}, }}, }, }, }} snap := Summarize(res) if len(snap.Projects) != 1 { t.Fatalf("project count = %d", len(snap.Projects)) } p := snap.Projects[0] if p.ContainerCount != 2 || p.RunningCount != 1 || p.ServiceCount != 2 { t.Fatalf("counts = svc=%d c=%d run=%d", p.ServiceCount, p.ContainerCount, p.RunningCount) } if p.Services[0].Name != "db" { // sorted alphabetically t.Fatalf("first service = %q", p.Services[0].Name) } } func TestValidProjectAction(t *testing.T) { for _, a := range []string{"up", "down", "stop", "restart", "pull"} { if _, ok := ValidProjectAction(a); !ok { t.Errorf("%q should be valid", a) } } if _, ok := ValidProjectAction("rm"); ok { t.Error("rm should be rejected") } } func TestManageProjectRejectsUnknownAction(t *testing.T) { if _, err := ManageProject(context.Background(), ProjectRefs{Name: "x", WorkingDir: "/tmp"}, "bogus"); err == nil { t.Fatal("expected error for unknown action") } }