Files
HODA/internal/docker/client_test.go
T
TMCandClaude Opus 5.5 9d6a921c34
CI / build (push) Successful in 30s
Start HODA: a homelab dashboard built from Docker labels
Apps opt in with sema.dashboard.* labels in their own compose file. The
primary reads its host's containers through a read-only socket proxy,
follows Docker events, and merges in what the agent on the second host
reports. Icons come from the icon label, the dashboard-icons set or the
app's favicon, with a letter as the fallback.

Go with only the standard library, a distroless image, and a Gitea
Actions workflow copied from FiMa's: checks in the Dockerfile's check
stage, a Trivy scan, and a push to git.mariosem.es/msemes/hoda.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 21:18:20 +01:00

83 lines
2.6 KiB
Go

package docker
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestListContainers(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/containers/json" {
http.NotFound(w, r)
return
}
var f map[string][]string
if err := json.Unmarshal([]byte(r.URL.Query().Get("filters")), &f); err != nil || f["label"][0] != "sema.dashboard.enable" {
t.Errorf("filters = %q, want the label key", r.URL.Query().Get("filters"))
}
if r.URL.Query().Get("all") != "1" {
t.Error("stopped containers are not requested")
}
fmt.Fprint(w, `[{"Id":"abc","Names":["/gitea"],"Image":"gitea/gitea","State":"running","Status":"Up","Labels":{"sema.dashboard.enable":"true"},"Ports":[]}]`)
}))
defer srv.Close()
c, err := New("tcp://" + strings.TrimPrefix(srv.URL, "http://"))
if err != nil {
t.Fatal(err)
}
list, err := c.ListContainers(context.Background(), "sema.dashboard.enable")
if err != nil {
t.Fatal(err)
}
if len(list) != 1 || list[0].Name() != "gitea" || list[0].Labels["sema.dashboard.enable"] != "true" {
t.Errorf("containers = %+v", list)
}
}
func TestListContainersError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Forbidden", http.StatusForbidden)
}))
defer srv.Close()
c, _ := New(srv.URL)
if _, err := c.ListContainers(context.Background(), "x"); err == nil || !strings.Contains(err.Error(), "403") {
t.Errorf("err = %v, want the 403 reported", err)
}
}
func TestEvents(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `{"Type":"container","Action":"start","Actor":{"ID":"abc"}}`)
fmt.Fprintln(w, `{"Type":"container","Action":"exec_start: sh -c true","Actor":{"ID":"abc"}}`)
}))
defer srv.Close()
c, _ := New(srv.URL)
var actions []string
err := c.Events(context.Background(), "sema.dashboard.enable", func(ev Event) { actions = append(actions, ev.Action) })
if err == nil {
t.Error("Events returned no error when the stream ended")
}
if len(actions) != 2 || actions[0] != "start" {
t.Errorf("actions = %q", actions)
}
}
func TestNew(t *testing.T) {
for _, host := range []string{"unix:///var/run/docker.sock", "tcp://socket-proxy:2375", "https://docker.lan:2376"} {
if _, err := New(host); err != nil {
t.Errorf("New(%q) = %v", host, err)
}
}
for _, host := range []string{"ssh://user@host", "tcp://", "socket-proxy:2375"} {
if _, err := New(host); err == nil {
t.Errorf("New(%q) accepted an unusable host", host)
}
}
}