From 3b8b1e6e32c2d0150d64f2056ec88a84a8dcd8c7 Mon Sep 17 00:00:00 2001 From: Aurora Gaffney Date: Fri, 22 Mar 2024 15:55:17 -0500 Subject: [PATCH] chore: switch to external ouroboros-mock --- .github/workflows/golangci-lint.yml | 2 +- .github/workflows/pr.yml | 10 +- connection_manager_test.go | 2 +- connection_test.go | 2 +- go.mod | 5 +- go.sum | 8 + internal/test/ouroboros_mock/connection.go | 251 --------------------- internal/test/ouroboros_mock/entry.go | 136 ----------- internal/test/ouroboros_mock/mock_test.go | 52 ----- protocol/handshake/client_test.go | 2 +- protocol/handshake/server_test.go | 2 +- 11 files changed, 25 insertions(+), 447 deletions(-) delete mode 100644 internal/test/ouroboros_mock/connection.go delete mode 100644 internal/test/ouroboros_mock/entry.go delete mode 100644 internal/test/ouroboros_mock/mock_test.go diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 81b3d105..e059eeb5 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 1.20.x + go-version: 1.21.x cache: false - name: golangci-lint uses: golangci/golangci-lint-action@v4 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 82004de8..41691fd8 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -4,21 +4,27 @@ on: jobs: run-tests: + strategy: + matrix: + go-version: [1.21.x, 1.22.x] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 1.20.x + go-version: ${{ matrix.go-version }} - run: | make test build-test-program: + strategy: + matrix: + go-version: [1.21.x, 1.22.x] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 1.20.x + go-version: ${{ matrix.go-version }} - run: | make diff --git a/connection_manager_test.go b/connection_manager_test.go index cf2687e3..0a3a9470 100644 --- a/connection_manager_test.go +++ b/connection_manager_test.go @@ -20,7 +20,7 @@ import ( "time" ouroboros "github.com/blinklabs-io/gouroboros" - "github.com/blinklabs-io/gouroboros/internal/test/ouroboros_mock" + "github.com/blinklabs-io/ouroboros-mock" "github.com/blinklabs-io/gouroboros/protocol/keepalive" "go.uber.org/goleak" ) diff --git a/connection_test.go b/connection_test.go index 31162eb7..f904b6ad 100644 --- a/connection_test.go +++ b/connection_test.go @@ -20,7 +20,7 @@ import ( "time" ouroboros "github.com/blinklabs-io/gouroboros" - "github.com/blinklabs-io/gouroboros/internal/test/ouroboros_mock" + "github.com/blinklabs-io/ouroboros-mock" "go.uber.org/goleak" ) diff --git a/go.mod b/go.mod index a61671cd..098e4f9d 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,11 @@ module github.com/blinklabs-io/gouroboros -go 1.20 +go 1.21 + +toolchain go1.21.5 require ( + github.com/blinklabs-io/ouroboros-mock v0.1.0 github.com/fxamacker/cbor/v2 v2.6.0 github.com/jinzhu/copier v0.4.0 github.com/utxorpc/go-codegen v0.4.4 diff --git a/go.sum b/go.sum index a7f94346..d8102dcf 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,17 @@ +github.com/blinklabs-io/ouroboros-mock v0.1.0 h1:CvRnJAUMzzZVaY4CoqDedbRXyNXtZpzIkxK9KXNjdVI= +github.com/blinklabs-io/ouroboros-mock v0.1.0/go.mod h1:t9eIDjmj339GJtfV7jandJnCqmj8WkZsFg2N1TR68io= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/utxorpc/go-codegen v0.4.4 h1:NnRFkqKJ9ZkZ3pffpD3bb6K0IU6IaDXbkVD/DzDmuKs= github.com/utxorpc/go-codegen v0.4.4/go.mod h1:NWAVa5/vHWgSrp/wJzXnMpbaBeST0PmWCU5vX63hZdw= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -17,6 +23,8 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/test/ouroboros_mock/connection.go b/internal/test/ouroboros_mock/connection.go deleted file mode 100644 index 04a2988d..00000000 --- a/internal/test/ouroboros_mock/connection.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2023 Blink Labs Software -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ouroboros_mock - -import ( - "bytes" - "fmt" - "net" - "reflect" - "sync" - "time" - - "github.com/blinklabs-io/gouroboros/cbor" - "github.com/blinklabs-io/gouroboros/muxer" -) - -// ProtocolRole is an enum of the protocol roles -type ProtocolRole uint - -// Protocol roles -const ( - ProtocolRoleNone ProtocolRole = 0 // Default (invalid) protocol role - ProtocolRoleClient ProtocolRole = 1 // Client protocol role - ProtocolRoleServer ProtocolRole = 2 // Server protocol role -) - -// Connection mocks an Ouroboros connection -type Connection struct { - mockConn net.Conn - conn net.Conn - conversation []ConversationEntry - muxer *muxer.Muxer - muxerRecvChan chan *muxer.Segment - doneChan chan any - onceClose sync.Once -} - -// NewConnection returns a new Connection with the provided conversation entries -func NewConnection( - protocolRole ProtocolRole, - conversation []ConversationEntry, -) net.Conn { - c := &Connection{ - conversation: conversation, - doneChan: make(chan any), - } - c.conn, c.mockConn = net.Pipe() - // Start a muxer on the mocked side of the connection - c.muxer = muxer.New(c.mockConn) - // The muxer is for the opposite end of the connection, so we flip the protocol role - muxerProtocolRole := muxer.ProtocolRoleResponder - if protocolRole == ProtocolRoleServer { - muxerProtocolRole = muxer.ProtocolRoleInitiator - } - // We use ProtocolUnknown to catch all inbound messages when no other protocols are registered - _, c.muxerRecvChan, _ = c.muxer.RegisterProtocol( - muxer.ProtocolUnknown, - muxerProtocolRole, - ) - c.muxer.Start() - // Start async muxer error handler - go func() { - err, ok := <-c.muxer.ErrorChan() - if !ok { - return - } - panic(fmt.Sprintf("muxer error: %s", err)) - }() - // Start async conversation handler - go c.asyncLoop() - return c -} - -// Read provides a proxy to the client-side connection's Read function. This is needed to satisfy the net.Conn interface -func (c *Connection) Read(b []byte) (n int, err error) { - return c.conn.Read(b) -} - -// Write provides a proxy to the client-side connection's Write function. This is needed to satisfy the net.Conn interface -func (c *Connection) Write(b []byte) (n int, err error) { - return c.conn.Write(b) -} - -// Close closes both sides of the connection. This is needed to satisfy the net.Conn interface -func (c *Connection) Close() error { - var retErr error - c.onceClose.Do(func() { - close(c.doneChan) - c.muxer.Stop() - if err := c.conn.Close(); err != nil { - retErr = err - return - } - if err := c.mockConn.Close(); err != nil { - retErr = err - return - } - }) - return retErr -} - -// LocalAddr provides a proxy to the client-side connection's LocalAddr function. This is needed to satisfy the net.Conn interface -func (c *Connection) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -// RemoteAddr provides a proxy to the client-side connection's RemoteAddr function. This is needed to satisfy the net.Conn interface -func (c *Connection) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} - -// SetDeadline provides a proxy to the client-side connection's SetDeadline function. This is needed to satisfy the net.Conn interface -func (c *Connection) SetDeadline(t time.Time) error { - return c.conn.SetDeadline(t) -} - -// SetReadDeadline provides a proxy to the client-side connection's SetReadDeadline function. This is needed to satisfy the net.Conn interface -func (c *Connection) SetReadDeadline(t time.Time) error { - return c.conn.SetReadDeadline(t) -} - -// SetWriteDeadline provides a proxy to the client-side connection's SetWriteDeadline function. This is needed to satisfy the net.Conn interface -func (c *Connection) SetWriteDeadline(t time.Time) error { - return c.conn.SetWriteDeadline(t) -} - -func (c *Connection) asyncLoop() { - for _, entry := range c.conversation { - select { - case <-c.doneChan: - return - default: - } - switch entry.Type { - case EntryTypeInput: - if err := c.processInputEntry(entry); err != nil { - panic(err.Error()) - } - case EntryTypeOutput: - if err := c.processOutputEntry(entry); err != nil { - panic(fmt.Sprintf("output error: %s", err)) - } - case EntryTypeClose: - c.Close() - case EntryTypeSleep: - time.Sleep(entry.Duration) - default: - panic( - fmt.Sprintf( - "unknown conversation entry type: %d: %#v", - entry.Type, - entry, - ), - ) - } - } -} - -func (c *Connection) processInputEntry(entry ConversationEntry) error { - // Wait for segment to be received from muxer - segment, ok := <-c.muxerRecvChan - if !ok { - return nil - } - if segment.GetProtocolId() != entry.ProtocolId { - return fmt.Errorf( - "input message protocol ID did not match expected value: expected %d, got %d", - entry.ProtocolId, - segment.GetProtocolId(), - ) - } - if segment.IsResponse() != entry.IsResponse { - return fmt.Errorf( - "input message response flag did not match expected value: expected %v, got %v", - entry.IsResponse, - segment.IsResponse(), - ) - } - // Determine message type - msgType, err := cbor.DecodeIdFromList(segment.Payload) - if err != nil { - return fmt.Errorf("decode error: %s", err) - } - if entry.InputMessage != nil { - // Create Message object from CBOR - msg, err := entry.MsgFromCborFunc(uint(msgType), segment.Payload) - if err != nil { - return fmt.Errorf("message from CBOR error: %s", err) - } - if msg == nil { - return fmt.Errorf("received unknown message type: %d", msgType) - } - - // Compare received message to expected message, excluding the cbor content - // - // As changing the CBOR of the expected message is not thread-safe, we instead clear the - // CBOR of the received message - msg.SetCbor(nil) - if !reflect.DeepEqual(msg, entry.InputMessage) { - return fmt.Errorf( - "parsed message does not match expected value: got %#v, expected %#v", - msg, - entry.InputMessage, - ) - } - } else { - if entry.InputMessageType == uint(msgType) { - return nil - } - return fmt.Errorf("input message is not of expected type: expected %d, got %d", entry.InputMessageType, msgType) - } - return nil -} - -func (c *Connection) processOutputEntry(entry ConversationEntry) error { - payloadBuf := bytes.NewBuffer(nil) - for _, msg := range entry.OutputMessages { - // Get raw CBOR from message - data := msg.Cbor() - // If message has no raw CBOR, encode the message - if data == nil { - var err error - data, err = cbor.Encode(msg) - if err != nil { - return err - } - } - payloadBuf.Write(data) - } - segment := muxer.NewSegment( - entry.ProtocolId, - payloadBuf.Bytes(), - entry.IsResponse, - ) - if err := c.muxer.Send(segment); err != nil { - return err - } - return nil -} diff --git a/internal/test/ouroboros_mock/entry.go b/internal/test/ouroboros_mock/entry.go deleted file mode 100644 index b7a71a88..00000000 --- a/internal/test/ouroboros_mock/entry.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2023 Blink Labs Software -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ouroboros_mock - -import ( - "time" - - "github.com/blinklabs-io/gouroboros/protocol" - "github.com/blinklabs-io/gouroboros/protocol/handshake" - "github.com/blinklabs-io/gouroboros/protocol/keepalive" -) - -const ( - MockNetworkMagic uint32 = 999999 - MockProtocolVersionNtC uint16 = (14 + protocol.ProtocolVersionNtCOffset) - MockProtocolVersionNtN uint16 = 13 - MockKeepAliveCookie uint16 = 999 -) - -type EntryType int - -const ( - EntryTypeNone EntryType = 0 - EntryTypeInput EntryType = 1 - EntryTypeOutput EntryType = 2 - EntryTypeClose EntryType = 3 - EntryTypeSleep EntryType = 4 -) - -type ConversationEntry struct { - Type EntryType - ProtocolId uint16 - IsResponse bool - OutputMessages []protocol.Message - InputMessage protocol.Message - InputMessageType uint - MsgFromCborFunc protocol.MessageFromCborFunc - Duration time.Duration -} - -// ConversationEntryHandshakeRequestGeneric is a pre-defined conversation event that matches a generic -// handshake request from a client -var ConversationEntryHandshakeRequestGeneric = ConversationEntry{ - Type: EntryTypeInput, - ProtocolId: handshake.ProtocolId, - InputMessageType: handshake.MessageTypeProposeVersions, -} - -// ConversationEntryHandshakeNtCResponse is a pre-defined conversation entry for a server NtC handshake response -var ConversationEntryHandshakeNtCResponse = ConversationEntry{ - Type: EntryTypeOutput, - ProtocolId: handshake.ProtocolId, - IsResponse: true, - OutputMessages: []protocol.Message{ - handshake.NewMsgAcceptVersion( - MockProtocolVersionNtC, - protocol.VersionDataNtC9to14(MockNetworkMagic), - ), - }, -} - -// ConversationEntryHandshakeNtNResponse is a pre-defined conversation entry for a server NtN handshake response -var ConversationEntryHandshakeNtNResponse = ConversationEntry{ - Type: EntryTypeOutput, - ProtocolId: handshake.ProtocolId, - IsResponse: true, - OutputMessages: []protocol.Message{ - handshake.NewMsgAcceptVersion( - MockProtocolVersionNtN, - protocol.VersionDataNtN13andUp{ - VersionDataNtN11to12: protocol.VersionDataNtN11to12{ - CborNetworkMagic: MockNetworkMagic, - CborInitiatorAndResponderDiffusionMode: protocol.DiffusionModeInitiatorOnly, - CborPeerSharing: protocol.PeerSharingModeNoPeerSharing, - CborQuery: protocol.QueryModeDisabled, - }, - }, - ), - }, -} - -// ConversationEntryKeepAliveRequest is a pre-defined conversation entry for a keep-alive request -var ConversationEntryKeepAliveRequest = ConversationEntry{ - Type: EntryTypeInput, - ProtocolId: keepalive.ProtocolId, - InputMessage: keepalive.NewMsgKeepAlive(MockKeepAliveCookie), - MsgFromCborFunc: keepalive.NewMsgFromCbor, -} - -// ConversationEntryKeepAliveResponse is a pre-defined conversation entry for a keep-alive response -var ConversationEntryKeepAliveResponse = ConversationEntry{ - Type: EntryTypeOutput, - ProtocolId: keepalive.ProtocolId, - IsResponse: true, - OutputMessages: []protocol.Message{ - keepalive.NewMsgKeepAliveResponse(MockKeepAliveCookie), - }, -} - -// ConversationKeepAlive is a pre-defined conversation with a NtN handshake and repeated keep-alive requests -// and responses -var ConversationKeepAlive = []ConversationEntry{ - ConversationEntryHandshakeRequestGeneric, - ConversationEntryHandshakeNtNResponse, - ConversationEntryKeepAliveRequest, - ConversationEntryKeepAliveResponse, - ConversationEntryKeepAliveRequest, - ConversationEntryKeepAliveResponse, - ConversationEntryKeepAliveRequest, - ConversationEntryKeepAliveResponse, - ConversationEntryKeepAliveRequest, - ConversationEntryKeepAliveResponse, -} - -// ConversationKeepAliveClose is a pre-defined conversation with a NtN handshake that will close the connection -// after receiving a keep-alive request -var ConversationKeepAliveClose = []ConversationEntry{ - ConversationEntryHandshakeRequestGeneric, - ConversationEntryHandshakeNtNResponse, - ConversationEntryKeepAliveRequest, - ConversationEntry{ - Type: EntryTypeClose, - }, -} diff --git a/internal/test/ouroboros_mock/mock_test.go b/internal/test/ouroboros_mock/mock_test.go deleted file mode 100644 index d464e71c..00000000 --- a/internal/test/ouroboros_mock/mock_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023 Blink Labs Software -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ouroboros_mock - -import ( - "testing" - "time" - - ouroboros "github.com/blinklabs-io/gouroboros" - "go.uber.org/goleak" -) - -// Basic test of conversation mock functionality -func TestBasic(t *testing.T) { - defer goleak.VerifyNone(t) - mockConn := NewConnection( - ProtocolRoleClient, - []ConversationEntry{ - ConversationEntryHandshakeRequestGeneric, - ConversationEntryHandshakeNtCResponse, - }, - ) - oConn, err := ouroboros.New( - ouroboros.WithConnection(mockConn), - ouroboros.WithNetworkMagic(MockNetworkMagic), - ) - if err != nil { - t.Fatalf("unexpected error when creating Ouroboros object: %s", err) - } - // Close Ouroboros connection - if err := oConn.Close(); err != nil { - t.Fatalf("unexpected error when closing Ouroboros object: %s", err) - } - // Wait for connection shutdown - select { - case <-oConn.ErrorChan(): - case <-time.After(10 * time.Second): - t.Errorf("did not shutdown within timeout") - } -} diff --git a/protocol/handshake/client_test.go b/protocol/handshake/client_test.go index 6cf0a745..f9c08e91 100644 --- a/protocol/handshake/client_test.go +++ b/protocol/handshake/client_test.go @@ -21,9 +21,9 @@ import ( "time" ouroboros "github.com/blinklabs-io/gouroboros" - "github.com/blinklabs-io/gouroboros/internal/test/ouroboros_mock" "github.com/blinklabs-io/gouroboros/protocol" "github.com/blinklabs-io/gouroboros/protocol/handshake" + "github.com/blinklabs-io/ouroboros-mock" "go.uber.org/goleak" ) diff --git a/protocol/handshake/server_test.go b/protocol/handshake/server_test.go index d1276540..7834b8c0 100644 --- a/protocol/handshake/server_test.go +++ b/protocol/handshake/server_test.go @@ -20,7 +20,7 @@ import ( "time" ouroboros "github.com/blinklabs-io/gouroboros" - "github.com/blinklabs-io/gouroboros/internal/test/ouroboros_mock" + "github.com/blinklabs-io/ouroboros-mock" "github.com/blinklabs-io/gouroboros/protocol" "github.com/blinklabs-io/gouroboros/protocol/handshake" "go.uber.org/goleak"