a73x

internal/agent/syncclient/exposures_test.go

Ref:   Size: 2.9 KiB   History

package syncclient

import (
	"testing"

	"github.com/a73x/eitri/internal/pb"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// fakeExposures records what it was asked to converge and answers with a fixed
// report.
type fakeExposures struct {
	sawIDs []string
	calls  int
	out    []*pb.ExposureStatus
}

func (f *fakeExposures) Converge(desired []*pb.ExposureSpec) []*pb.ExposureStatus {
	f.calls++
	f.sawIDs = nil
	for _, d := range desired {
		f.sawIDs = append(f.sawIDs, d.GetId())
	}
	return f.out
}

func TestConvergeExposuresPassesTheSnapshotThrough(t *testing.T) {
	fe := &fakeExposures{out: []*pb.ExposureStatus{{Id: "e1", State: "active"}}}
	c := &Client{Exposures: fe}

	got := c.convergeExposures(&pb.Snapshot{
		Exposures: []*pb.ExposureSpec{{Id: "e1", VmId: "vm1", GuestPort: 8080, HostPort: 30080}},
	})

	assert.Equal(t, []string{"e1"}, fe.sawIDs)
	require.Len(t, got, 1)
	assert.Equal(t, "active", got[0].GetState())
}

func TestConvergeExposuresWithoutAProxyConvergesNothing(t *testing.T) {
	c := &Client{}
	assert.Nil(t, c.convergeExposures(&pb.Snapshot{
		Exposures: []*pb.ExposureSpec{{Id: "e1"}},
	}), "an agent with no proxy publishes nothing, and says so by saying nothing")
}

func TestConvergeExposuresRefusesAFencedSnapshot(t *testing.T) {
	fe := &fakeExposures{}
	c := &Client{Exposures: fe}

	got := c.reportExposures(&pb.Snapshot{
		Exposures: []*pb.ExposureSpec{{Id: "e1"}},
	}, &pb.Report{FenceViolation: true})

	assert.Zero(t, fe.calls, "a snapshot the engine refused must not drive the listeners")
	assert.Nil(t, got)
}

// The third state: the engine refused the snapshot for naming a floor above
// this build, but did NOT fence it. Exposures still converge — their fields are
// ones this agent reads in full — so a host one release behind keeps its
// published ports instead of dropping every live forward. This test is what
// stops a future refusal path from quietly setting FenceViolation and taking
// the port-forwards down with it.
func TestConvergeExposuresStillRunsForAVersionRefusedSnapshot(t *testing.T) {
	fe := &fakeExposures{out: []*pb.ExposureStatus{{Id: "e1", State: "active"}}}
	c := &Client{Exposures: fe}

	// What Engine.Step returns when it refuses on min_agent_version: the epoch
	// is accepted, no VM was acted on, and FenceViolation is false.
	rep := &pb.Report{LastSeenEpoch: 1, FenceViolation: false,
		Vms: []*pb.VMStatus{{VmId: "vm1", Phase: "failed", LastError: "agent v0.0.6 is below this snapshot's floor v0.0.7"}}}

	got := c.reportExposures(&pb.Snapshot{
		MinAgentVersion: "v0.0.7",
		Exposures:       []*pb.ExposureSpec{{Id: "e1", VmId: "vm1", GuestPort: 8080, HostPort: 30080}},
	}, rep)

	assert.Equal(t, 1, fe.calls, "a version-refused snapshot is current, so its exposures still converge")
	assert.Equal(t, []string{"e1"}, fe.sawIDs)
	require.Len(t, got, 1)
	assert.Equal(t, "active", got[0].GetState())
}