test(broker): use generated port mocks
- commit
- eebe943bcf78776ae8af40778f95bd8b1270ae7d
- author
- brice · · 2026-09-23 14:49 UTC
- committer
- brice · · 2026-09-23 14:49 UTC
- parent
- 357815c
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +33 -61
M internal/app/ui_driver_acceptance_test.go +6 -4
| @@ -11,14 +11,14 @@ import ( | |||
| 11 | 11 | "github.com/bnema/vev/internal/adapters/uiterm" | |
| 12 | 12 | "github.com/bnema/vev/internal/domain" | |
| 13 | 13 | "github.com/bnema/vev/internal/ports" | |
| 14 | + | portsmocks "github.com/bnema/vev/internal/ports/mocks" | |
| 14 | 15 | "github.com/bnema/vev/internal/protocol" | |
| 15 | 16 | "github.com/bnema/vev/internal/usecase/client" | |
| 17 | + | "github.com/stretchr/testify/mock" | |
| 16 | 18 | "github.com/stretchr/testify/require" | |
| 17 | 19 | ) | |
| 18 | 20 | ||
| 19 | - | // scriptedBrokerConnector adapts one scripted broker service to the connector | |
| 20 | - | // port, so a driver test can run the whole JSONL composition over a deterministic | |
| 21 | - | // committed publication without a broker process. | |
| 21 | + | // scriptedBrokerConnector is also used by broker_client_test.go, outside this change's scope. | |
| 22 | 22 | type scriptedBrokerConnector struct{ service *terminalCompositionService } | |
| 23 | 23 | ||
| 24 | 24 | func (c scriptedBrokerConnector) Connect(context.Context) (ports.BrokerService, error) { | |
| @@ -46,9 +46,11 @@ func startScriptedUIDriverRun(t *testing.T, service *terminalCompositionService, | |||
| 46 | 46 | t.Error("the scripted UI driver did not stop") | |
| 47 | 47 | } | |
| 48 | 48 | }) | |
| 49 | + | connector := portsmocks.NewMockBrokerConnector(t) | |
| 50 | + | connector.EXPECT().Connect(mock.Anything).Return(service, nil).Maybe() | |
| 49 | 51 | go func() { | |
| 50 | 52 | run.done <- runUIDriverClient(ctx, brokerClientConfig{ | |
| 51 | - | Connector: scriptedBrokerConnector{service: service}, | |
| 53 | + | Connector: connector, | |
| 52 | 54 | Terminal: terminal, | |
| 53 | 55 | Clock: clock.New(), | |
| 54 | 56 | UI: ui, | |
| @@ -11,14 +11,14 @@ import ( | |||||
| 11 | "github.com/bnema/vev/internal/adapters/uiterm" | 11 | "github.com/bnema/vev/internal/adapters/uiterm" | ||
| 12 | "github.com/bnema/vev/internal/domain" | 12 | "github.com/bnema/vev/internal/domain" | ||
| 13 | "github.com/bnema/vev/internal/ports" | 13 | "github.com/bnema/vev/internal/ports" | ||
| 14 | + | portsmocks "github.com/bnema/vev/internal/ports/mocks" | |||
| 14 | "github.com/bnema/vev/internal/protocol" | 15 | "github.com/bnema/vev/internal/protocol" | ||
| 15 | "github.com/bnema/vev/internal/usecase/client" | 16 | "github.com/bnema/vev/internal/usecase/client" | ||
| 17 | + | "github.com/stretchr/testify/mock" | |||
| 16 | "github.com/stretchr/testify/require" | 18 | "github.com/stretchr/testify/require" | ||
| 17 | ) | 19 | ) | ||
| 18 | 20 | ||||
| 19 | - | // scriptedBrokerConnector adapts one scripted broker service to the connector | 21 | + | // scriptedBrokerConnector is also used by broker_client_test.go, outside this change's scope. |
| 20 | - | // port, so a driver test can run the whole JSONL composition over a deterministic | |||
| 21 | - | // committed publication without a broker process. | |||
| 22 | type scriptedBrokerConnector struct{ service *terminalCompositionService } | 22 | type scriptedBrokerConnector struct{ service *terminalCompositionService } | ||
| 23 | 23 | ||||
| 24 | func (c scriptedBrokerConnector) Connect(context.Context) (ports.BrokerService, error) { | 24 | func (c scriptedBrokerConnector) Connect(context.Context) (ports.BrokerService, error) { | ||
| @@ -46,9 +46,11 @@ func startScriptedUIDriverRun(t *testing.T, service *terminalCompositionService, | |||||
| 46 | t.Error("the scripted UI driver did not stop") | 46 | t.Error("the scripted UI driver did not stop") | ||
| 47 | } | 47 | } | ||
| 48 | }) | 48 | }) | ||
| 49 | + | connector := portsmocks.NewMockBrokerConnector(t) | |||
| 50 | + | connector.EXPECT().Connect(mock.Anything).Return(service, nil).Maybe() | |||
| 49 | go func() { | 51 | go func() { | ||
| 50 | run.done <- runUIDriverClient(ctx, brokerClientConfig{ | 52 | run.done <- runUIDriverClient(ctx, brokerClientConfig{ | ||
| 51 | - | Connector: scriptedBrokerConnector{service: service}, | 53 | + | Connector: connector, |
| 52 | Terminal: terminal, | 54 | Terminal: terminal, | ||
| 53 | Clock: clock.New(), | 55 | Clock: clock.New(), | ||
| 54 | UI: ui, | 56 | UI: ui, | ||
M internal/app/web_daemon_broker_test.go +14 -41
| @@ -21,7 +21,9 @@ import ( | |||
| 21 | 21 | "github.com/bnema/vev/internal/adapters/brokeripc" | |
| 22 | 22 | "github.com/bnema/vev/internal/adapters/webterm" | |
| 23 | 23 | "github.com/bnema/vev/internal/ports" | |
| 24 | + | portsmocks "github.com/bnema/vev/internal/ports/mocks" | |
| 24 | 25 | "github.com/bnema/vev/internal/usecase/client" | |
| 26 | + | "github.com/stretchr/testify/mock" | |
| 25 | 27 | ) | |
| 26 | 28 | ||
| 27 | 29 | // Browser gateway broker composition tests (Plan 001 P7.4d). | |
| @@ -50,35 +52,6 @@ const ( | |||
| 50 | 52 | ||
| 51 | 53 | var webGatewayReadyPID = regexp.MustCompile(`READY-(\d+)`) | |
| 52 | 54 | ||
| 53 | - | // webConnectorFunc adapts one function to the broker connector port, so a test | |
| 54 | - | // can script successive attempts without a broker process. | |
| 55 | - | type webConnectorFunc func(context.Context) (ports.BrokerService, error) | |
| 56 | - | ||
| 57 | - | func (f webConnectorFunc) Connect(ctx context.Context) (ports.BrokerService, error) { return f(ctx) } | |
| 58 | - | ||
| 59 | - | // webScriptedConnector serves one scripted broker service and then fails every | |
| 60 | - | // later attempt, so a broker loss cannot silently reconnect into the scripted | |
| 61 | - | // publication. | |
| 62 | - | type webScriptedConnector struct { | |
| 63 | - | mu sync.Mutex | |
| 64 | - | service ports.BrokerService | |
| 65 | - | err error | |
| 66 | - | calls int | |
| 67 | - | } | |
| 68 | - | ||
| 69 | - | func (c *webScriptedConnector) Connect(context.Context) (ports.BrokerService, error) { | |
| 70 | - | c.mu.Lock() | |
| 71 | - | defer c.mu.Unlock() | |
| 72 | - | c.calls++ | |
| 73 | - | if c.calls == 1 && c.service != nil { | |
| 74 | - | return c.service, nil | |
| 75 | - | } | |
| 76 | - | if c.err == nil { | |
| 77 | - | c.err = errors.New("broker is gone") | |
| 78 | - | } | |
| 79 | - | return nil, c.err | |
| 80 | - | } | |
| 81 | - | ||
| 82 | 55 | // webFrame is one gateway websocket frame: the transactional VT update plus the | |
| 83 | 56 | // mouse-tracking flag. | |
| 84 | 57 | type webFrame struct { | |
| @@ -603,10 +576,10 @@ func TestWebGatewayTabClosingIsIsolated(t *testing.T) { | |||
| 603 | 576 | // keeps serving frames. | |
| 604 | 577 | func TestWebGatewayTabStaysPickerWhenDestinationIsRefused(t *testing.T) { | |
| 605 | 578 | service := newTerminalCompositionService(ports.BrokerSnapshot{Epoch: terminalCompositionEpoch, Revision: 1}) | |
| 579 | + | connector := portsmocks.NewMockBrokerConnector(t) | |
| 580 | + | connector.EXPECT().Connect(mock.Anything).Return(service, nil).Maybe() | |
| 606 | 581 | fixture := startWebGatewayFixtureOver(t, offlineClientFixture{}, func() ports.BrokerConnector { | |
| 607 | - | return webConnectorFunc(func(context.Context) (ports.BrokerService, error) { | |
| 608 | - | return service, nil | |
| 609 | - | }) | |
| 582 | + | return connector | |
| 610 | 583 | }) | |
| 611 | 584 | ||
| 612 | 585 | tab := fixture.openTab(t) | |
| @@ -633,7 +606,14 @@ func TestWebGatewayTabStaysPickerWhenDestinationIsRefused(t *testing.T) { | |||
| 633 | 606 | // and keeps serving frames while it retries. | |
| 634 | 607 | func TestWebGatewayTabReturnsToPickerOnBrokerLoss(t *testing.T) { | |
| 635 | 608 | service := newTerminalCompositionService(terminalCompositionSnapshot([]string{"work"}, nil)) | |
| 636 | - | connector := &webScriptedConnector{service: service} | |
| 609 | + | connector := portsmocks.NewMockBrokerConnector(t) | |
| 610 | + | var calls atomic.Int32 | |
| 611 | + | connector.EXPECT().Connect(mock.Anything).RunAndReturn(func(context.Context) (ports.BrokerService, error) { | |
| 612 | + | if calls.Add(1) == 1 { | |
| 613 | + | return service, nil | |
| 614 | + | } | |
| 615 | + | return nil, errors.New("broker is gone") | |
| 616 | + | }).Maybe() | |
| 637 | 617 | fixture := startWebGatewayFixtureOver(t, offlineClientFixture{}, func() ports.BrokerConnector { return connector }) | |
| 638 | 618 | ||
| 639 | 619 | tab := fixture.openTab(t) | |
| @@ -655,14 +635,7 @@ func TestWebGatewayTabReturnsToPickerOnBrokerLoss(t *testing.T) { | |||
| 655 | 635 | t.Fatalf("broker loss ended the browser run: %v", tab.readerErr()) | |
| 656 | 636 | default: | |
| 657 | 637 | } | |
| 658 | - | require.GreaterOrEqual(t, connector.attempts(), 2, "a lost connection must be retried, never fatal") | |
| 659 | - | } | |
| 660 | - | ||
| 661 | - | // attempts reports how many connect attempts the scripted connector observed. | |
| 662 | - | func (c *webScriptedConnector) attempts() int { | |
| 663 | - | c.mu.Lock() | |
| 664 | - | defer c.mu.Unlock() | |
| 665 | - | return c.calls | |
| 638 | + | require.GreaterOrEqual(t, calls.Load(), int32(2), "a lost connection must be retried, never fatal") | |
| 666 | 639 | } | |
| 667 | 640 | ||
| 668 | 641 | // TestWebCompositionUsesTheBrokerConnector pins the production composition | |
| @@ -21,7 +21,9 @@ import ( | |||||
| 21 | "github.com/bnema/vev/internal/adapters/brokeripc" | 21 | "github.com/bnema/vev/internal/adapters/brokeripc" | ||
| 22 | "github.com/bnema/vev/internal/adapters/webterm" | 22 | "github.com/bnema/vev/internal/adapters/webterm" | ||
| 23 | "github.com/bnema/vev/internal/ports" | 23 | "github.com/bnema/vev/internal/ports" | ||
| 24 | + | portsmocks "github.com/bnema/vev/internal/ports/mocks" | |||
| 24 | "github.com/bnema/vev/internal/usecase/client" | 25 | "github.com/bnema/vev/internal/usecase/client" | ||
| 26 | + | "github.com/stretchr/testify/mock" | |||
| 25 | ) | 27 | ) | ||
| 26 | 28 | ||||
| 27 | // Browser gateway broker composition tests (Plan 001 P7.4d). | 29 | // Browser gateway broker composition tests (Plan 001 P7.4d). | ||
| @@ -50,35 +52,6 @@ const ( | |||||
| 50 | 52 | ||||
| 51 | var webGatewayReadyPID = regexp.MustCompile(`READY-(\d+)`) | 53 | var webGatewayReadyPID = regexp.MustCompile(`READY-(\d+)`) | ||
| 52 | 54 | ||||
| 53 | - | // webConnectorFunc adapts one function to the broker connector port, so a test | |||
| 54 | - | // can script successive attempts without a broker process. | |||
| 55 | - | type webConnectorFunc func(context.Context) (ports.BrokerService, error) | |||
| 56 | - | ||||
| 57 | - | func (f webConnectorFunc) Connect(ctx context.Context) (ports.BrokerService, error) { return f(ctx) } | |||
| 58 | - | ||||
| 59 | - | // webScriptedConnector serves one scripted broker service and then fails every | |||
| 60 | - | // later attempt, so a broker loss cannot silently reconnect into the scripted | |||
| 61 | - | // publication. | |||
| 62 | - | type webScriptedConnector struct { | |||
| 63 | - | mu sync.Mutex | |||
| 64 | - | service ports.BrokerService | |||
| 65 | - | err error | |||
| 66 | - | calls int | |||
| 67 | - | } | |||
| 68 | - | ||||
| 69 | - | func (c *webScriptedConnector) Connect(context.Context) (ports.BrokerService, error) { | |||
| 70 | - | c.mu.Lock() | |||
| 71 | - | defer c.mu.Unlock() | |||
| 72 | - | c.calls++ | |||
| 73 | - | if c.calls == 1 && c.service != nil { | |||
| 74 | - | return c.service, nil | |||
| 75 | - | } | |||
| 76 | - | if c.err == nil { | |||
| 77 | - | c.err = errors.New("broker is gone") | |||
| 78 | - | } | |||
| 79 | - | return nil, c.err | |||
| 80 | - | } | |||
| 81 | - | ||||
| 82 | // webFrame is one gateway websocket frame: the transactional VT update plus the | 55 | // webFrame is one gateway websocket frame: the transactional VT update plus the | ||
| 83 | // mouse-tracking flag. | 56 | // mouse-tracking flag. | ||
| 84 | type webFrame struct { | 57 | type webFrame struct { | ||
| @@ -603,10 +576,10 @@ func TestWebGatewayTabClosingIsIsolated(t *testing.T) { | |||||
| 603 | // keeps serving frames. | 576 | // keeps serving frames. | ||
| 604 | func TestWebGatewayTabStaysPickerWhenDestinationIsRefused(t *testing.T) { | 577 | func TestWebGatewayTabStaysPickerWhenDestinationIsRefused(t *testing.T) { | ||
| 605 | service := newTerminalCompositionService(ports.BrokerSnapshot{Epoch: terminalCompositionEpoch, Revision: 1}) | 578 | service := newTerminalCompositionService(ports.BrokerSnapshot{Epoch: terminalCompositionEpoch, Revision: 1}) | ||
| 579 | + | connector := portsmocks.NewMockBrokerConnector(t) | |||
| 580 | + | connector.EXPECT().Connect(mock.Anything).Return(service, nil).Maybe() | |||
| 606 | fixture := startWebGatewayFixtureOver(t, offlineClientFixture{}, func() ports.BrokerConnector { | 581 | fixture := startWebGatewayFixtureOver(t, offlineClientFixture{}, func() ports.BrokerConnector { | ||
| 607 | - | return webConnectorFunc(func(context.Context) (ports.BrokerService, error) { | 582 | + | return connector |
| 608 | - | return service, nil | |||
| 609 | - | }) | |||
| 610 | }) | 583 | }) | ||
| 611 | 584 | ||||
| 612 | tab := fixture.openTab(t) | 585 | tab := fixture.openTab(t) | ||
| @@ -633,7 +606,14 @@ func TestWebGatewayTabStaysPickerWhenDestinationIsRefused(t *testing.T) { | |||||
| 633 | // and keeps serving frames while it retries. | 606 | // and keeps serving frames while it retries. | ||
| 634 | func TestWebGatewayTabReturnsToPickerOnBrokerLoss(t *testing.T) { | 607 | func TestWebGatewayTabReturnsToPickerOnBrokerLoss(t *testing.T) { | ||
| 635 | service := newTerminalCompositionService(terminalCompositionSnapshot([]string{"work"}, nil)) | 608 | service := newTerminalCompositionService(terminalCompositionSnapshot([]string{"work"}, nil)) | ||
| 636 | - | connector := &webScriptedConnector{service: service} | 609 | + | connector := portsmocks.NewMockBrokerConnector(t) |
| 610 | + | var calls atomic.Int32 | |||
| 611 | + | connector.EXPECT().Connect(mock.Anything).RunAndReturn(func(context.Context) (ports.BrokerService, error) { | |||
| 612 | + | if calls.Add(1) == 1 { | |||
| 613 | + | return service, nil | |||
| 614 | + | } | |||
| 615 | + | return nil, errors.New("broker is gone") | |||
| 616 | + | }).Maybe() | |||
| 637 | fixture := startWebGatewayFixtureOver(t, offlineClientFixture{}, func() ports.BrokerConnector { return connector }) | 617 | fixture := startWebGatewayFixtureOver(t, offlineClientFixture{}, func() ports.BrokerConnector { return connector }) | ||
| 638 | 618 | ||||
| 639 | tab := fixture.openTab(t) | 619 | tab := fixture.openTab(t) | ||
| @@ -655,14 +635,7 @@ func TestWebGatewayTabReturnsToPickerOnBrokerLoss(t *testing.T) { | |||||
| 655 | t.Fatalf("broker loss ended the browser run: %v", tab.readerErr()) | 635 | t.Fatalf("broker loss ended the browser run: %v", tab.readerErr()) | ||
| 656 | default: | 636 | default: | ||
| 657 | } | 637 | } | ||
| 658 | - | require.GreaterOrEqual(t, connector.attempts(), 2, "a lost connection must be retried, never fatal") | 638 | + | require.GreaterOrEqual(t, calls.Load(), int32(2), "a lost connection must be retried, never fatal") |
| 659 | - | } | |||
| 660 | - | ||||
| 661 | - | // attempts reports how many connect attempts the scripted connector observed. | |||
| 662 | - | func (c *webScriptedConnector) attempts() int { | |||
| 663 | - | c.mu.Lock() | |||
| 664 | - | defer c.mu.Unlock() | |||
| 665 | - | return c.calls | |||
| 666 | } | 639 | } | ||
| 667 | 640 | ||||
| 668 | // TestWebCompositionUsesTheBrokerConnector pins the production composition | 641 | // TestWebCompositionUsesTheBrokerConnector pins the production composition | ||
M internal/usecase/broker/pool_test.go +13 -16
| @@ -10,7 +10,9 @@ import ( | |||
| 10 | 10 | ||
| 11 | 11 | "github.com/bnema/vev/internal/domain" | |
| 12 | 12 | "github.com/bnema/vev/internal/ports" | |
| 13 | + | portsmocks "github.com/bnema/vev/internal/ports/mocks" | |
| 13 | 14 | "github.com/bnema/vev/internal/protocol" | |
| 15 | + | "github.com/stretchr/testify/mock" | |
| 14 | 16 | "github.com/stretchr/testify/require" | |
| 15 | 17 | ) | |
| 16 | 18 | ||
| @@ -84,12 +86,14 @@ func poolRequest(id ports.BrokerConnectionID, n uint64) ports.BrokerOpenStreamRe | |||
| 84 | 86 | func setupPool(t testing.TB, connector poolConnector) (*Pool, *manualClock) { | |
| 85 | 87 | t.Helper() | |
| 86 | 88 | clock := newManualClock(time.Unix(0, 0)) | |
| 87 | - | resolver := poolResolver(func(_ context.Context, r ports.BrokerOpenStreamRequest) (ports.BrokerDialTarget, error) { | |
| 89 | + | resolver := portsmocks.NewMockBrokerRouteAuthority(t) | |
| 90 | + | resolver.EXPECT().ResolveDialTarget(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, r ports.BrokerOpenStreamRequest) (ports.BrokerDialTarget, error) { | |
| 88 | 91 | return ports.BrokerDialTarget{Fence: ports.BrokerEndpointFence{Local: true}, Policy: r.Policy, Address: "fake", StartMode: r.StartMode, ExpectedIdentity: ports.BrokerExpectedIdentity{Identity: "canonical", Bound: true}}, nil | |
| 89 | - | }) | |
| 90 | - | binder := poolBinder(func(_ context.Context, request ports.BrokerIdentityBindingRequest) (ports.BrokerDaemonIdentity, error) { | |
| 92 | + | }).Maybe() | |
| 93 | + | binder := portsmocks.NewMockBrokerIdentityBinder(t) | |
| 94 | + | binder.EXPECT().BindAuthenticatedIdentity(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, request ports.BrokerIdentityBindingRequest) (ports.BrokerDaemonIdentity, error) { | |
| 91 | 95 | return request.Identity, nil | |
| 92 | - | }) | |
| 96 | + | }).Maybe() | |
| 93 | 97 | p, err := NewPool(1, resolver, binder, connector, clock, PoolLimits{Physical: 2, Clients: 128, Streams: 128, StreamsPerClient: 128, Warm: 2, Idle: time.Minute}) | |
| 94 | 98 | require.NoError(t, err) | |
| 95 | 99 | t.Cleanup(func() { require.NoError(t, p.Close()) }) | |
| @@ -109,17 +113,10 @@ func await(t *testing.T, ch <-chan struct{}) { | |||
| 109 | 113 | // a physical transport, so an unprovisioned or conflicting request can never | |
| 110 | 114 | // reach an address. The same ordering fences a refused local request. | |
| 111 | 115 | func TestPoolRefusedResolutionNeverDials(t *testing.T) { | |
| 112 | - | var dials atomic.Int32 | |
| 113 | - | resolver := poolResolver(func(context.Context, ports.BrokerOpenStreamRequest) (ports.BrokerDialTarget, error) { | |
| 114 | - | return ports.BrokerDialTarget{}, ports.BrokerError{Code: ports.BrokerErrorConflictingPolicy, Cause: errors.New("refused")} | |
| 115 | - | }) | |
| 116 | - | connector := poolConnector(func(context.Context, ports.BrokerDialTarget) (ports.BrokerPhysicalConnection, error) { | |
| 117 | - | dials.Add(1) | |
| 118 | - | return nil, nil | |
| 119 | - | }) | |
| 120 | - | binder := poolBinder(func(_ context.Context, request ports.BrokerIdentityBindingRequest) (ports.BrokerDaemonIdentity, error) { | |
| 121 | - | return request.Identity, nil | |
| 122 | - | }) | |
| 116 | + | resolver := portsmocks.NewMockBrokerRouteAuthority(t) | |
| 117 | + | resolver.EXPECT().ResolveDialTarget(mock.Anything, mock.Anything).Return(ports.BrokerDialTarget{}, ports.BrokerError{Code: ports.BrokerErrorConflictingPolicy, Cause: errors.New("refused")}).Once() | |
| 118 | + | connector := portsmocks.NewMockBrokerEndpointConnector(t) | |
| 119 | + | binder := portsmocks.NewMockBrokerIdentityBinder(t) | |
| 123 | 120 | pool, err := NewPool(1, resolver, binder, connector, newManualClock(time.Unix(0, 0)), PoolLimits{Physical: 1, Clients: 8, Streams: 8, StreamsPerClient: 8, Warm: 1, Idle: time.Minute}) | |
| 124 | 121 | require.NoError(t, err) | |
| 125 | 122 | t.Cleanup(func() { require.NoError(t, pool.Close()) }) | |
| @@ -131,7 +128,7 @@ func TestPoolRefusedResolutionNeverDials(t *testing.T) { | |||
| 131 | 128 | var typed ports.BrokerError | |
| 132 | 129 | require.ErrorAs(t, err, &typed) | |
| 133 | 130 | require.Equal(t, ports.BrokerErrorConflictingPolicy, typed.Code) | |
| 134 | - | require.Zero(t, dials.Load(), "a refused request must never dial") | |
| 131 | + | connector.AssertNotCalled(t, "Connect", mock.Anything, mock.Anything) | |
| 135 | 132 | } | |
| 136 | 133 | ||
| 137 | 134 | // TestPoolOpenFailureWithTypedNilConnection proves a failed open that still | |
| @@ -10,7 +10,9 @@ import ( | |||||
| 10 | 10 | ||||
| 11 | "github.com/bnema/vev/internal/domain" | 11 | "github.com/bnema/vev/internal/domain" | ||
| 12 | "github.com/bnema/vev/internal/ports" | 12 | "github.com/bnema/vev/internal/ports" | ||
| 13 | + | portsmocks "github.com/bnema/vev/internal/ports/mocks" | |||
| 13 | "github.com/bnema/vev/internal/protocol" | 14 | "github.com/bnema/vev/internal/protocol" | ||
| 15 | + | "github.com/stretchr/testify/mock" | |||
| 14 | "github.com/stretchr/testify/require" | 16 | "github.com/stretchr/testify/require" | ||
| 15 | ) | 17 | ) | ||
| 16 | 18 | ||||
| @@ -84,12 +86,14 @@ func poolRequest(id ports.BrokerConnectionID, n uint64) ports.BrokerOpenStreamRe | |||||
| 84 | func setupPool(t testing.TB, connector poolConnector) (*Pool, *manualClock) { | 86 | func setupPool(t testing.TB, connector poolConnector) (*Pool, *manualClock) { | ||
| 85 | t.Helper() | 87 | t.Helper() | ||
| 86 | clock := newManualClock(time.Unix(0, 0)) | 88 | clock := newManualClock(time.Unix(0, 0)) | ||
| 87 | - | resolver := poolResolver(func(_ context.Context, r ports.BrokerOpenStreamRequest) (ports.BrokerDialTarget, error) { | 89 | + | resolver := portsmocks.NewMockBrokerRouteAuthority(t) |
| 90 | + | resolver.EXPECT().ResolveDialTarget(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, r ports.BrokerOpenStreamRequest) (ports.BrokerDialTarget, error) { | |||
| 88 | return ports.BrokerDialTarget{Fence: ports.BrokerEndpointFence{Local: true}, Policy: r.Policy, Address: "fake", StartMode: r.StartMode, ExpectedIdentity: ports.BrokerExpectedIdentity{Identity: "canonical", Bound: true}}, nil | 91 | return ports.BrokerDialTarget{Fence: ports.BrokerEndpointFence{Local: true}, Policy: r.Policy, Address: "fake", StartMode: r.StartMode, ExpectedIdentity: ports.BrokerExpectedIdentity{Identity: "canonical", Bound: true}}, nil | ||
| 89 | - | }) | 92 | + | }).Maybe() |
| 90 | - | binder := poolBinder(func(_ context.Context, request ports.BrokerIdentityBindingRequest) (ports.BrokerDaemonIdentity, error) { | 93 | + | binder := portsmocks.NewMockBrokerIdentityBinder(t) |
| 94 | + | binder.EXPECT().BindAuthenticatedIdentity(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, request ports.BrokerIdentityBindingRequest) (ports.BrokerDaemonIdentity, error) { | |||
| 91 | return request.Identity, nil | 95 | return request.Identity, nil | ||
| 92 | - | }) | 96 | + | }).Maybe() |
| 93 | p, err := NewPool(1, resolver, binder, connector, clock, PoolLimits{Physical: 2, Clients: 128, Streams: 128, StreamsPerClient: 128, Warm: 2, Idle: time.Minute}) | 97 | p, err := NewPool(1, resolver, binder, connector, clock, PoolLimits{Physical: 2, Clients: 128, Streams: 128, StreamsPerClient: 128, Warm: 2, Idle: time.Minute}) | ||
| 94 | require.NoError(t, err) | 98 | require.NoError(t, err) | ||
| 95 | t.Cleanup(func() { require.NoError(t, p.Close()) }) | 99 | t.Cleanup(func() { require.NoError(t, p.Close()) }) | ||
| @@ -109,17 +113,10 @@ func await(t *testing.T, ch <-chan struct{}) { | |||||
| 109 | // a physical transport, so an unprovisioned or conflicting request can never | 113 | // a physical transport, so an unprovisioned or conflicting request can never | ||
| 110 | // reach an address. The same ordering fences a refused local request. | 114 | // reach an address. The same ordering fences a refused local request. | ||
| 111 | func TestPoolRefusedResolutionNeverDials(t *testing.T) { | 115 | func TestPoolRefusedResolutionNeverDials(t *testing.T) { | ||
| 112 | - | var dials atomic.Int32 | 116 | + | resolver := portsmocks.NewMockBrokerRouteAuthority(t) |
| 113 | - | resolver := poolResolver(func(context.Context, ports.BrokerOpenStreamRequest) (ports.BrokerDialTarget, error) { | 117 | + | resolver.EXPECT().ResolveDialTarget(mock.Anything, mock.Anything).Return(ports.BrokerDialTarget{}, ports.BrokerError{Code: ports.BrokerErrorConflictingPolicy, Cause: errors.New("refused")}).Once() |
| 114 | - | return ports.BrokerDialTarget{}, ports.BrokerError{Code: ports.BrokerErrorConflictingPolicy, Cause: errors.New("refused")} | 118 | + | connector := portsmocks.NewMockBrokerEndpointConnector(t) |
| 115 | - | }) | 119 | + | binder := portsmocks.NewMockBrokerIdentityBinder(t) |
| 116 | - | connector := poolConnector(func(context.Context, ports.BrokerDialTarget) (ports.BrokerPhysicalConnection, error) { | |||
| 117 | - | dials.Add(1) | |||
| 118 | - | return nil, nil | |||
| 119 | - | }) | |||
| 120 | - | binder := poolBinder(func(_ context.Context, request ports.BrokerIdentityBindingRequest) (ports.BrokerDaemonIdentity, error) { | |||
| 121 | - | return request.Identity, nil | |||
| 122 | - | }) | |||
| 123 | pool, err := NewPool(1, resolver, binder, connector, newManualClock(time.Unix(0, 0)), PoolLimits{Physical: 1, Clients: 8, Streams: 8, StreamsPerClient: 8, Warm: 1, Idle: time.Minute}) | 120 | pool, err := NewPool(1, resolver, binder, connector, newManualClock(time.Unix(0, 0)), PoolLimits{Physical: 1, Clients: 8, Streams: 8, StreamsPerClient: 8, Warm: 1, Idle: time.Minute}) | ||
| 124 | require.NoError(t, err) | 121 | require.NoError(t, err) | ||
| 125 | t.Cleanup(func() { require.NoError(t, pool.Close()) }) | 122 | t.Cleanup(func() { require.NoError(t, pool.Close()) }) | ||
| @@ -131,7 +128,7 @@ func TestPoolRefusedResolutionNeverDials(t *testing.T) { | |||||
| 131 | var typed ports.BrokerError | 128 | var typed ports.BrokerError | ||
| 132 | require.ErrorAs(t, err, &typed) | 129 | require.ErrorAs(t, err, &typed) | ||
| 133 | require.Equal(t, ports.BrokerErrorConflictingPolicy, typed.Code) | 130 | require.Equal(t, ports.BrokerErrorConflictingPolicy, typed.Code) | ||
| 134 | - | require.Zero(t, dials.Load(), "a refused request must never dial") | 131 | + | connector.AssertNotCalled(t, "Connect", mock.Anything, mock.Anything) |
| 135 | } | 132 | } | ||
| 136 | 133 | ||||
| 137 | // TestPoolOpenFailureWithTypedNilConnection proves a failed open that still | 134 | // TestPoolOpenFailureWithTypedNilConnection proves a failed open that still | ||