258 lines
9.1 KiB
Go
258 lines
9.1 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"browser.local/platform/domain"
|
|
"browser.local/platform/repo"
|
|
)
|
|
|
|
func TestCoreServiceRegistersNewRunControlSession(t *testing.T) {
|
|
svc := newTestCoreService()
|
|
|
|
result, err := svc.RegisterRunHello(validRunControlHello())
|
|
if err != nil {
|
|
t.Fatalf("register hello: %v", err)
|
|
}
|
|
if !result.Accepted || result.SessionToken == "" || result.HeartbeatIntervalSeconds <= 0 {
|
|
t.Fatalf("expected accepted hello response, got %+v", result)
|
|
}
|
|
|
|
endpoint, err := svc.GetRunEndpoint("run-local")
|
|
if err != nil {
|
|
t.Fatalf("get registered endpoint: %v", err)
|
|
}
|
|
if endpoint.Status != domain.RunEndpointStatusOnline || !endpoint.LastHeartbeatAt.Equal(fixedTime) {
|
|
t.Fatalf("expected online endpoint with heartbeat time, got %+v", endpoint)
|
|
}
|
|
if len(endpoint.Capabilities) != 3 || endpoint.Capacity.MaxJobs != 4 {
|
|
t.Fatalf("expected capabilities and capacity, got %+v", endpoint)
|
|
}
|
|
}
|
|
|
|
func TestCoreServiceDoesNotRevokeSharedLegacyRunEndpoint(t *testing.T) {
|
|
svc := newTestCoreService()
|
|
plugin, endpoint := createPluginAndRunEndpoint(t, svc)
|
|
hello := validRunControlHello()
|
|
hello.CapabilityReport.Capabilities = append(hello.CapabilityReport.Capabilities, plugin.RequiredRunCapabilities...)
|
|
registered, err := svc.RegisterRunHello(hello)
|
|
if err != nil || !registered.Accepted {
|
|
t.Fatalf("register shared endpoint: result=%+v err=%v", registered, err)
|
|
}
|
|
first, err := svc.CreateServerInstance(domain.ServerInstance{ID: "shared-first", PluginID: plugin.ID, RunEndpointID: endpoint.ID, Name: "Shared First", State: domain.ServerInstanceStateReady})
|
|
if err != nil {
|
|
t.Fatalf("create first legacy server: %v", err)
|
|
}
|
|
if _, err := svc.CreateServerInstance(domain.ServerInstance{ID: "shared-second", PluginID: plugin.ID, RunEndpointID: endpoint.ID, Name: "Shared Second", State: domain.ServerInstanceStateReady}); err != nil {
|
|
t.Fatalf("create second legacy server: %v", err)
|
|
}
|
|
if err := svc.revokeRunControlSessionForInstance(first); err != nil {
|
|
t.Fatalf("revoke first legacy Run: %v", err)
|
|
}
|
|
session, err := svc.store.RunControlSessions().Get(endpoint.ID)
|
|
if err != nil || session.Status != domain.AuthSessionStatusActive {
|
|
t.Fatalf("shared legacy endpoint session must remain active, session=%+v err=%v", session, err)
|
|
}
|
|
}
|
|
|
|
func TestCoreServiceReRegistersExistingRunEndpoint(t *testing.T) {
|
|
svc := newTestCoreService()
|
|
first, err := svc.RegisterRunHello(validRunControlHello())
|
|
if err != nil {
|
|
t.Fatalf("register first hello: %v", err)
|
|
}
|
|
|
|
hello := validRunControlHello()
|
|
hello.DisplayName = "Local Run Updated"
|
|
hello.Version = "0.2.0"
|
|
hello.CapabilityReport.Capabilities = []string{"control.hello", "control.heartbeat", "jobs.claim"}
|
|
hello.CapabilityReport.Fingerprint = "cap-v2"
|
|
second, err := svc.RegisterRunHello(hello)
|
|
if err != nil {
|
|
t.Fatalf("register second hello: %v", err)
|
|
}
|
|
if second.SessionToken == first.SessionToken {
|
|
t.Fatalf("expected re-registration to issue a new token, got %q", second.SessionToken)
|
|
}
|
|
|
|
endpoint, err := svc.GetRunEndpoint("run-local")
|
|
if err != nil {
|
|
t.Fatalf("get re-registered endpoint: %v", err)
|
|
}
|
|
if endpoint.DisplayName != "Local Run Updated" || endpoint.Version != "0.2.0" || len(endpoint.Capabilities) != 3 {
|
|
t.Fatalf("expected endpoint metadata update, got %+v", endpoint)
|
|
}
|
|
}
|
|
|
|
func TestCoreServiceAcceptsRunHeartbeat(t *testing.T) {
|
|
svc := newTestCoreService()
|
|
hello, err := svc.RegisterRunHello(validRunControlHello())
|
|
if err != nil {
|
|
t.Fatalf("register hello: %v", err)
|
|
}
|
|
|
|
result, err := svc.AcceptRunHeartbeat(domain.RunControlHeartbeat{
|
|
RunEndpointID: "run-local",
|
|
SessionToken: hello.SessionToken,
|
|
Version: "0.1.1",
|
|
Status: domain.RunEndpointStatusDegraded,
|
|
CapabilityFingerprint: "cap-v1",
|
|
Capacity: domain.RunCapacity{MaxJobs: 4, RunningJobs: 2, QueuedJobs: 1},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("accept heartbeat: %v", err)
|
|
}
|
|
if !result.Accepted || result.RefreshCapabilities {
|
|
t.Fatalf("expected accepted heartbeat without refresh, got %+v", result)
|
|
}
|
|
|
|
endpoint, err := svc.GetRunEndpoint("run-local")
|
|
if err != nil {
|
|
t.Fatalf("get heartbeat endpoint: %v", err)
|
|
}
|
|
if endpoint.Status != domain.RunEndpointStatusDegraded || endpoint.Version != "0.1.1" || endpoint.Capacity.RunningJobs != 2 {
|
|
t.Fatalf("expected heartbeat metadata update, got %+v", endpoint)
|
|
}
|
|
}
|
|
|
|
func TestCoreServiceRejectsInvalidRunHeartbeatToken(t *testing.T) {
|
|
svc := newTestCoreService()
|
|
if _, err := svc.RegisterRunHello(validRunControlHello()); err != nil {
|
|
t.Fatalf("register hello: %v", err)
|
|
}
|
|
|
|
_, err := svc.AcceptRunHeartbeat(domain.RunControlHeartbeat{
|
|
RunEndpointID: "run-local",
|
|
SessionToken: "stale-token",
|
|
Version: "0.1.1",
|
|
Status: domain.RunEndpointStatusOnline,
|
|
CapabilityFingerprint: "cap-v1",
|
|
Capacity: domain.RunCapacity{MaxJobs: 4, RunningJobs: 3},
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "sessionToken is invalid") {
|
|
t.Fatalf("expected invalid token rejection, got %v", err)
|
|
}
|
|
|
|
endpoint, err := svc.GetRunEndpoint("run-local")
|
|
if err != nil {
|
|
t.Fatalf("get endpoint after rejected heartbeat: %v", err)
|
|
}
|
|
if endpoint.Capacity.RunningJobs != 0 || endpoint.Version != "0.1.0" {
|
|
t.Fatalf("heartbeat with invalid token must not update endpoint, got %+v", endpoint)
|
|
}
|
|
}
|
|
|
|
func TestCoreServiceRejectsInvalidRunControlHello(t *testing.T) {
|
|
svc := newTestCoreService()
|
|
invalid := validRunControlHello()
|
|
invalid.RegistrationToken = ""
|
|
invalid.Capacity.RunningJobs = 8
|
|
|
|
_, err := svc.RegisterRunHello(invalid)
|
|
if err == nil || !strings.Contains(err.Error(), "registrationToken") || !strings.Contains(err.Error(), "runningJobs") {
|
|
t.Fatalf("expected validation errors, got %v", err)
|
|
}
|
|
if _, err := svc.GetRunEndpoint("run-local"); !errors.Is(err, repo.ErrNotFound) {
|
|
t.Fatalf("invalid hello must not create endpoint, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCoreServiceRunHelloRejectsStalePackageKeyAfterReset(t *testing.T) {
|
|
svc, session, instance := newDistributionTestFixture(t)
|
|
distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{
|
|
ServerInstanceID: instance.ID,
|
|
TargetOS: "linux",
|
|
TargetArch: "amd64",
|
|
IdempotencyKey: "idem-control-auth",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("generate run distribution: %v", err)
|
|
}
|
|
pkg := readGeneratedPackageConfig(t, svc, session, distribution.ArtifactID)
|
|
hello := validRunControlHello()
|
|
hello.RunEndpointID = instance.RunEndpointID
|
|
hello.RegistrationToken = pkg.AuthKey
|
|
hello.ServerInstanceID = instance.ID
|
|
hello.PluginID = instance.PluginID
|
|
hello.ComponentKind = domain.DistributionComponentRun
|
|
hello.KeyGeneration = pkg.KeyGeneration
|
|
|
|
result, err := svc.RegisterRunHello(hello)
|
|
if err != nil {
|
|
t.Fatalf("register current package hello: %v", err)
|
|
}
|
|
if !result.Accepted || result.SessionToken == "" {
|
|
t.Fatalf("expected current package hello to be accepted, got %+v", result)
|
|
}
|
|
|
|
if _, err := svc.ResetComponentKeyForSession(session, domain.ComponentKeyResetRequest{
|
|
ServerInstanceID: instance.ID,
|
|
ComponentKind: domain.DistributionComponentRun,
|
|
}); err != nil {
|
|
t.Fatalf("reset run key: %v", err)
|
|
}
|
|
result, err = svc.RegisterRunHello(hello)
|
|
if err != nil {
|
|
t.Fatalf("register stale package hello: %v", err)
|
|
}
|
|
if result.Accepted || result.SessionToken != "" {
|
|
t.Fatalf("expected stale package hello to be rejected, got %+v", result)
|
|
}
|
|
}
|
|
|
|
func TestCoreServiceRequestsCapabilityRefreshOnFingerprintDrift(t *testing.T) {
|
|
svc := newTestCoreService()
|
|
hello, err := svc.RegisterRunHello(validRunControlHello())
|
|
if err != nil {
|
|
t.Fatalf("register hello: %v", err)
|
|
}
|
|
|
|
result, err := svc.AcceptRunHeartbeat(domain.RunControlHeartbeat{
|
|
RunEndpointID: "run-local",
|
|
SessionToken: hello.SessionToken,
|
|
Version: "0.1.0",
|
|
Status: domain.RunEndpointStatusOnline,
|
|
CapabilityFingerprint: "cap-v2",
|
|
Capacity: domain.RunCapacity{MaxJobs: 4},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("accept drift heartbeat: %v", err)
|
|
}
|
|
if !result.RefreshCapabilities {
|
|
t.Fatalf("expected capability refresh request, got %+v", result)
|
|
}
|
|
|
|
result, err = svc.AcceptRunHeartbeat(domain.RunControlHeartbeat{
|
|
RunEndpointID: "run-local",
|
|
SessionToken: hello.SessionToken,
|
|
Version: "0.1.0",
|
|
Status: domain.RunEndpointStatusOnline,
|
|
CapabilityFingerprint: "cap-v2",
|
|
Capacity: domain.RunCapacity{MaxJobs: 4},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("accept stable heartbeat: %v", err)
|
|
}
|
|
if result.RefreshCapabilities {
|
|
t.Fatalf("expected refreshed fingerprint to become known, got %+v", result)
|
|
}
|
|
}
|
|
|
|
func validRunControlHello() domain.RunControlHello {
|
|
return domain.RunControlHello{
|
|
RegistrationToken: "registration-token",
|
|
RunEndpointID: "run-local",
|
|
DisplayName: "Local Run",
|
|
Version: "0.1.0",
|
|
Status: domain.RunEndpointStatusOnline,
|
|
Platform: "darwin/arm64",
|
|
CapabilityReport: domain.RunCapabilityReport{
|
|
Capabilities: []string{"control.hello", "control.heartbeat", domain.JobCapabilityDistributionBuild},
|
|
Fingerprint: "cap-v1",
|
|
},
|
|
Capacity: domain.RunCapacity{MaxJobs: 4},
|
|
}
|
|
}
|