Files
package-updater/internal/master/model/model_test.go
shcizo 061243f5da feat(store): drift state, move candidates and service merge (#10)
Adds services.awaiting_inventory, Service.Drifted/RunsRef/RunningRef, AcceptRunning, MoveCandidates, MergeSources and MergeService, and maps a job for a deleted service to ErrNotFound.
2026-10-13 10:28:34 +02:00

156 lines
5.2 KiB
Go

package model
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/shcizo/package-updater/internal/protocol"
)
func TestLocationKey(t *testing.T) {
cases := []struct {
name, agentKey, want string
}{
{"compose is per agent", "compose|slusk|web", "compose|7|slusk|web"},
{"container is per agent", "container|adhoc", "container|7|adhoc"},
{"swarm is per cluster", "swarm|gitea_gitea", "swarm|cluster-1|gitea_gitea"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert.Equal(t, c.want, LocationKey(7, "cluster-1", c.agentKey))
})
}
}
func TestDisplayName(t *testing.T) {
cases := []struct {
name string
svc Service
want string
}{
{"compose", Service{Kind: protocol.KindCompose, ComposeProject: "slusk", ComposeService: "web"}, "slusk/web"},
{"swarm", Service{Kind: protocol.KindSwarm, SwarmService: "gitea_gitea"}, "gitea_gitea"},
{"container", Service{Kind: protocol.KindContainer, ContainerName: "adhoc"}, "adhoc"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert.Equal(t, c.want, c.svc.DisplayName())
})
}
}
func TestImageVersionRef(t *testing.T) {
cases := []struct {
name string
v ImageVersion
want string
}{
{"tag and digest", ImageVersion{Repo: "r/x", Tag: "1", Digest: "sha256:a"}, "r/x:1@sha256:a"},
{"tag only", ImageVersion{Repo: "r/x", Tag: "1"}, "r/x:1"},
{"digest only", ImageVersion{Repo: "r/x", Digest: "sha256:a"}, "r/x@sha256:a"},
{"registry port", ImageVersion{Repo: "localhost:5000/x", Tag: "1"}, "localhost:5000/x:1"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert.Equal(t, c.want, c.v.Ref())
})
}
}
const (
dA = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
dB = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
)
func TestRunsRef(t *testing.T) {
cases := []struct {
name string
desired, actualRef, actualDig string
want bool
}{
{"same string", "repo:v1@" + dA, "repo:v1@" + dA, "", true},
{"tag and digest, actual reports it separately", "repo:tag@" + dA, "repo:tag", dA, true},
{"bare image reports latest digest", "postgres:latest@" + dA, "postgres", dA, true},
{"tagless actual with the same digest", "repo:tag@" + dA, "repo@" + dA, "", true},
{"same digest under another tag", "repo:v1@" + dA, "repo:v2@" + dA, "", true},
{"same digest, actual tag differs, digest reported", "repo:v1@" + dA, "repo:v2", dA, true},
{"same tag, actual digest unknown", "repo:tag@" + dA, "repo:tag", "", true},
{"desired without digest, same tag", "repo:tag", "repo:tag@" + dA, "", true},
{"bare desired, latest actual", "repo", "repo:latest", "", true},
{"registry with port", "localhost:5000/foo:v1@" + dA, "localhost:5000/foo:v1", dA, true},
{"registry with port, other digest", "localhost:5000/foo:v1@" + dA, "localhost:5000/foo:v1", dB, false},
{"same tag, other digest", "repo:tag@" + dA, "repo:tag", dB, false},
{"other digest in the ref", "repo:tag@" + dA, "repo:tag@" + dB, "", false},
{"other tag, digest unknown", "repo:v1@" + dA, "repo:v2", "", false},
{"tagless desired, tagged actual, digest unknown", "repo@" + dA, "repo:v1", "", false},
{"other repo", "repo:tag@" + dA, "other:tag", dA, false},
{"no docker.io normalisation", "nginx:1@" + dA, "docker.io/library/nginx:1", dA, false},
{"case-sensitive repo", "Repo:1@" + dA, "repo:1", dA, false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert.Equal(t, c.want, RunsRef(c.desired, c.actualRef, c.actualDig))
})
}
}
func TestDrifted(t *testing.T) {
now := time.Now()
drifted := Service{DesiredRef: "repo:tag@" + dA, ActualRef: "repo:tag", ActualDigest: dB}
cases := []struct {
name string
mutate func(*Service)
want bool
}{
{"runs another digest", func(*Service) {}, true},
{"never updated", func(s *Service) { s.DesiredRef = "" }, false},
{"removed", func(s *Service) { s.RemovedAt = &now }, false},
{"awaiting inventory", func(s *Service) { s.AwaitingInventory = true }, false},
{"runs the desired digest", func(s *Service) { s.ActualDigest = dA }, false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
svc := drifted
c.mutate(&svc)
assert.Equal(t, c.want, svc.Drifted())
})
}
}
func TestRunningRef(t *testing.T) {
cases := []struct {
name, actualRef, actualDigest, want string
}{
{"ref with digest is kept", "repo:v1@" + dB, dA, "repo:v1@" + dB},
{"tag plus reported digest", "repo:v1", dA, "repo:v1@" + dA},
{"bare image becomes latest", "postgres", dA, "postgres:latest@" + dA},
{"registry with port", "localhost:5000/foo:v1", dA, "localhost:5000/foo:v1@" + dA},
{"no digest known", "repo:v1", "", "repo:v1"},
{"malformed digest", "repo:v1", "sha256:abc", "repo:v1"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert.Equal(t, c.want, RunningRef(c.actualRef, c.actualDigest))
})
}
}
func TestJob_Active(t *testing.T) {
cases := []struct {
status JobStatus
want bool
}{
{JobQueued, true},
{JobRunning, true},
{JobSucceeded, false},
{JobFailed, false},
}
for _, c := range cases {
t.Run(string(c.status), func(t *testing.T) {
assert.Equal(t, c.want, Job{Status: c.status}.Active())
})
}
}