internal/server/syncsvc/exposures_test.go
Ref: Size: 3.2 KiB History
package syncsvc
import (
"testing"
"github.com/a73x/eitri/internal/pb"
"github.com/a73x/eitri/internal/server/registry"
"github.com/a73x/eitri/internal/server/store"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// exposureVM places one live VM on the fixture's host.
func exposureVM(t *testing.T, f *fixture, id, name string) {
t.Helper()
require.NoError(t, f.st.CreateVM(store.VM{ID: id, HostID: f.host.ID, Name: name,
ImageURL: "u", ImageSHA256: "s", VCPUs: 1, MemMB: 512, DiskGB: 5, PowerState: "running"}))
}
// TestSnapshotCarriesExposures pins that a host's desired state names every
// exposure it should be running, at snapshot level rather than nested in a VM:
// exposures are their own objects converging on their own cadence.
func TestSnapshotCarriesExposures(t *testing.T) {
f := setup(t)
exposureVM(t, f, "vm1", "web-1")
e, err := f.st.CreateExposure("vm1", 8080, 30080, "tcp")
require.NoError(t, err)
snap, err := f.svc.buildSnapshot(f.host.ID)
require.NoError(t, err)
require.Len(t, snap.GetExposures(), 1)
got := snap.GetExposures()[0]
assert.Equal(t, e.ID, got.GetId())
assert.Equal(t, "vm1", got.GetVmId())
assert.Equal(t, uint32(8080), got.GetGuestPort())
assert.Equal(t, uint32(30080), got.GetHostPort())
assert.Equal(t, "tcp", got.GetProtocol())
}
// TestSnapshotDropsExposuresOfATombstonedVM pins that a listener stops being
// desired the moment its VM is on its way out, rather than when its row is
// finally reaped.
func TestSnapshotDropsExposuresOfATombstonedVM(t *testing.T) {
f := setup(t)
exposureVM(t, f, "vm1", "web-1")
_, err := f.st.CreateExposure("vm1", 8080, 30080, "tcp")
require.NoError(t, err)
require.NoError(t, f.st.TombstoneVM("vm1"))
snap, err := f.svc.buildSnapshot(f.host.ID)
require.NoError(t, err)
assert.Empty(t, snap.GetExposures())
}
// TestApplyReportFoldsExposureStateIntoTheRegistry pins the return leg: what a
// host says its listeners are doing is live state, held in memory beside the
// VM rows it reports in the same message.
func TestApplyReportFoldsExposureStateIntoTheRegistry(t *testing.T) {
f := setup(t)
f.svc.applyReport(f.host.ID, &pb.Report{
Exposures: []*pb.ExposureStatus{
{Id: "e1", State: "active"},
{Id: "e2", State: "failed", Reason: "listen tcp 0.0.0.0:30080: bind: address already in use"},
},
})
got, ok := f.reg.Get(f.host.ID)
require.True(t, ok)
require.Len(t, got.Report.Exposures, 2)
assert.Equal(t, registry.ExposureStatus{ID: "e1", State: "active"}, got.Report.Exposures[0])
assert.Equal(t, "failed", got.Report.Exposures[1].State)
assert.Contains(t, got.Report.Exposures[1].Reason, "address already in use")
}
// TestApplyReportRecordsTheHostUplinkAddress pins that the address an operator
// dials is durable, and that a report which carries none leaves it alone.
func TestApplyReportRecordsTheHostUplinkAddress(t *testing.T) {
f := setup(t)
f.svc.applyReport(f.host.ID, &pb.Report{HostUplinkAddr: "192.168.0.190"})
h, err := f.st.GetHost(f.host.ID)
require.NoError(t, err)
assert.Equal(t, "192.168.0.190", h.UplinkAddr)
f.svc.applyReport(f.host.ID, &pb.Report{})
h, err = f.st.GetHost(f.host.ID)
require.NoError(t, err)
assert.Equal(t, "192.168.0.190", h.UplinkAddr, "silence is not a statement")
}