diff --git a/command/root_test.go b/command/root_test.go index aa786e684..305afb291 100644 --- a/command/root_test.go +++ b/command/root_test.go @@ -54,7 +54,7 @@ func captureOutput(action func()) string { action() - w.Close() + _ = w.Close() out, _ := io.ReadAll(r) os.Stderr = rescueStderr diff --git a/consumer/http.go b/consumer/http.go index 77c36fd7a..303087f23 100644 --- a/consumer/http.go +++ b/consumer/http.go @@ -201,7 +201,7 @@ func (p *httpMockProvider) displayMismatches(t *testing.T, mismatches []native.M fmt.Println("\t\tDiff:") log.Println("[INFO] pact validation failed, errors: ") for _, m := range mismatches { - formattedRequest := fmt.Sprintf("%s %s", m.Request.Method, m.Request.Path) + formattedRequest := fmt.Sprintf("%s %s", m.Method, m.Path) switch m.Type { case "missing-request": fmt.Printf("\t\texpected: \t%s (Expected request that was not received)\n", formattedRequest) diff --git a/consumer/http_v2.go b/consumer/http_v2.go index 6d4c224fa..276a403ef 100644 --- a/consumer/http_v2.go +++ b/consumer/http_v2.go @@ -35,7 +35,7 @@ func NewV2Pact(config MockHTTPProviderConfig) (*V2HTTPMockProvider, error) { // AddInteraction to the pact func (p *V2HTTPMockProvider) AddInteraction() *V2UnconfiguredInteraction { log.Println("[DEBUG] pact add V2 interaction") - interaction := p.httpMockProvider.mockserver.NewInteraction("") + interaction := p.mockserver.NewInteraction("") i := &V2UnconfiguredInteraction{ interaction: &Interaction{ diff --git a/consumer/http_v3.go b/consumer/http_v3.go index bccce0bac..32d25fb9c 100644 --- a/consumer/http_v3.go +++ b/consumer/http_v3.go @@ -35,7 +35,7 @@ func NewV3Pact(config MockHTTPProviderConfig) (*V3HTTPMockProvider, error) { // AddInteraction to the pact func (p *V3HTTPMockProvider) AddInteraction() *V3UnconfiguredInteraction { log.Println("[DEBUG] pact add V3 interaction") - interaction := p.httpMockProvider.mockserver.NewInteraction("") + interaction := p.mockserver.NewInteraction("") i := &V3UnconfiguredInteraction{ interaction: &Interaction{ diff --git a/consumer/http_v4.go b/consumer/http_v4.go index 8dd986b79..55a79c9a4 100644 --- a/consumer/http_v4.go +++ b/consumer/http_v4.go @@ -36,7 +36,7 @@ func NewV4Pact(config MockHTTPProviderConfig) (*V4HTTPMockProvider, error) { // AddInteraction to the pact func (p *V4HTTPMockProvider) AddInteraction() *V4UnconfiguredInteraction { log.Println("[DEBUG] pact add V4 interaction") - interaction := p.httpMockProvider.mockserver.NewInteraction("") + interaction := p.mockserver.NewInteraction("") i := &V4UnconfiguredInteraction{ interaction: &Interaction{ @@ -92,6 +92,15 @@ func (i *V4UnconfiguredInteraction) UponReceiving(description string) *V4Unconfi return i } +// AddExternalReference records a reference to an external resource (such as a ticket or +// pull request) against the interaction. References appear under +// comments.references[group][name] in the Pact file. May be called multiple times. +func (i *V4UnconfiguredInteraction) AddExternalReference(group, name, value string) *V4UnconfiguredInteraction { + i.interaction.interaction.WithReference(group, name, value) + + return i +} + // WithRequest provides a builder for the expected request func (i *V4UnconfiguredInteraction) WithCompleteRequest(request Request) *V4InteractionWithCompleteRequest { i.interaction.WithCompleteRequest(request) diff --git a/consumer/http_v4_test.go b/consumer/http_v4_test.go index 33165496b..efb421bde 100644 --- a/consumer/http_v4_test.go +++ b/consumer/http_v4_test.go @@ -2,6 +2,7 @@ package consumer import ( "fmt" + "net/http" "os" "strings" "testing" @@ -85,6 +86,25 @@ func TestHttpV4TypeSystem(t *testing.T) { } +func TestV4HTTPAddExternalReference(t *testing.T) { + p, err := NewV4Pact(MockHTTPProviderConfig{ + Consumer: "consumer", + Provider: "provider", + }) + assert.NoError(t, err) + + err = p.AddInteraction(). + UponReceiving("a request with an external reference"). + AddExternalReference("Jira", "TICKET-123", "https://jira.example.com/browse/TICKET-123"). + WithRequest("GET", "/", func(b *V4RequestBuilder) {}). + WillRespondWith(200, func(b *V4ResponseBuilder) {}). + ExecuteTest(t, func(msc MockServerConfig) error { + _, err := http.Get(fmt.Sprintf("http://%s:%d/", msc.Host, msc.Port)) + return err + }) + assert.NoError(t, err) +} + var Like = matchers.Like var EachLike = matchers.EachLike var Term = matchers.Term diff --git a/examples/avro/codec.go b/examples/avro/codec.go index fb4d2aca8..4ad4798ac 100644 --- a/examples/avro/codec.go +++ b/examples/avro/codec.go @@ -1,11 +1,12 @@ package avro import ( - "os" + "github.com/linkedin/goavro/v2" ) +//nolint:unused // Retained as a reusable helper for the example package. func getCodec() *goavro.Codec { schema, err := os.ReadFile("user.avsc") if err != nil { diff --git a/examples/grpc/routeguide/server/server.go b/examples/grpc/routeguide/server/server.go index fa59e7ddf..0751994b1 100644 --- a/examples/grpc/routeguide/server/server.go +++ b/examples/grpc/routeguide/server/server.go @@ -40,9 +40,8 @@ import ( "github.com/pact-foundation/pact-go/v2/examples/grpc/routeguide/data" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" - "google.golang.org/grpc/status" - - "github.com/golang/protobuf/proto" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" pb "github.com/pact-foundation/pact-go/v2/examples/grpc/routeguide" ) @@ -55,6 +54,15 @@ var ( port = flag.Int("port", 50051, "The server port") ) +// Keep example flags/entrypoint available for standalone usage. +var ( + _ = tls + _ = certFile + _ = keyFile + _ = port + _ = main +) + type routeGuideServer struct { pb.UnimplementedRouteGuideServer savedFeatures []*pb.Feature // read-only after initialized @@ -247,7 +255,9 @@ func main() { } grpcServer := grpc.NewServer(opts...) pb.RegisterRouteGuideServer(grpcServer, NewServer()) - grpcServer.Serve(lis) + if err := grpcServer.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } } // exampleData is a copy of testdata/route_guide_db.json. It's to avoid diff --git a/installer/installer.go b/installer/installer.go index 30965e1fa..5e421d089 100644 --- a/installer/installer.go +++ b/installer/installer.go @@ -302,7 +302,7 @@ var setMacOSInstallName = func(file string) error { return fmt.Errorf("error setting install name on pact lib: %s", err) } - log.Println("[DEBUG] output from command", stdoutStderr) + log.Println("[DEBUG] output from command", string(stdoutStderr)) return err } @@ -388,7 +388,7 @@ const ( var packages = map[string]packageInfo{ FFIPackage: { libName: "libpact_ffi", - version: "0.4.28", + version: "0.5.6", semverRange: ">= 0.4.0, < 1.0.0", }, } @@ -417,13 +417,17 @@ func (d *defaultDownloader) download(src string, dst string) error { if err != nil { return fmt.Errorf("failed to create output file; %w", err) } - defer f.Close() + defer func() { + _ = f.Close() + }() resp, err := http.Get(src) if err != nil { return fmt.Errorf("failed http call to %s; %w", src, err) } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() archive, err := gzip.NewReader(resp.Body) if err != nil { @@ -523,7 +527,9 @@ func (d *defaultHasher) hash(src string) (string, error) { if err != nil { return "", err } - defer f.Close() + defer func() { + _ = f.Close() + }() h := md5.New() if _, err := io.Copy(h, f); err != nil { diff --git a/internal/native/message_server.go b/internal/native/message_server.go index bd6e6ec3a..e32b84cca 100644 --- a/internal/native/message_server.go +++ b/internal/native/message_server.go @@ -232,7 +232,7 @@ func (m *Message) WithContents(part interactionPart, contentType string, body [] defer free(cHeader) cBody := C.CString(string(body)) - defer free(cBody) + defer free(cBody) res := C.pactffi_with_body(m.handle, C.int(part), cHeader, cBody) log.Println("[DEBUG] response from pactffi_interaction_contents", (bool(res))) @@ -264,7 +264,7 @@ func (m *MessageServer) UsingPlugin(pluginName string, pluginVersion string) err return ErrHandleNotFound default: if res != 0 { - return fmt.Errorf("an unknown error (code: %v) occurred when adding a plugin for the test. Received error code:", res) + return fmt.Errorf("an unknown error (code: %v) occurred when adding a plugin for the test. Received error code", res) } } @@ -302,7 +302,7 @@ func (m *Message) WithPluginInteractionContents(part interactionPart, contentTyp return ErrPluginSpecificError default: if res != 0 { - return fmt.Errorf("an unknown error (code: %v) occurred when adding a plugin for the test. Received error code:", res) + return fmt.Errorf("an unknown error (code: %v) occurred when adding a plugin for the test. Received error code", res) } } @@ -543,10 +543,7 @@ func (m *MessageServer) WritePactFile(dir string, overwrite bool) error { cDir := C.CString(dir) defer free(cDir) - overwritePact := false - if overwrite { - overwritePact = true - } + overwritePact := overwrite res := int(C.pactffi_write_message_pact_file(m.messagePact.handle, cDir, C.bool(overwritePact))) @@ -572,10 +569,7 @@ func (m *MessageServer) WritePactFileForServer(port int, dir string, overwrite b cDir := C.CString(dir) defer free(cDir) - overwritePact := false - if overwrite { - overwritePact = true - } + overwritePact := overwrite res := int(C.pactffi_write_pact_file(C.int(port), cDir, C.bool(overwritePact))) @@ -596,3 +590,19 @@ func (m *MessageServer) WritePactFileForServer(port int, dir string, overwrite b return fmt.Errorf("an unknown error ocurred when writing to pact file") } } + +// WithReference records an external reference (e.g. a ticket or pull request) +// against the interaction. References are stored under comments.references[group][name] +// in the Pact file. This is a V4-only feature. +func (m *Message) WithReference(group, name, value string) *Message { + cGroup := C.CString(group) + defer free(cGroup) + cName := C.CString(name) + defer free(cName) + cValue := C.CString(value) + defer free(cValue) + + C.pactffi_add_interaction_reference(m.handle, cGroup, cName, cValue) + + return m +} diff --git a/internal/native/message_server_test.go b/internal/native/message_server_test.go index 0431b8e05..73f7d140b 100644 --- a/internal/native/message_server_test.go +++ b/internal/native/message_server_test.go @@ -406,7 +406,9 @@ func TestGrpcPluginInteraction(t *testing.T) { if err != nil { l.Fatalf("did not connect: %v", err) } - defer conn.Close() + defer func() { + _ = conn.Close() + }() c := NewPactPluginClient(conn) // Contact the server and print out its response. @@ -480,7 +482,9 @@ func TestGrpcPluginInteraction_ErrorResponse(t *testing.T) { if err != nil { l.Fatalf("did not connect: %v", err) } - defer conn.Close() + defer func() { + _ = conn.Close() + }() c := NewPactPluginClient(conn) // Contact the server and print out its response. diff --git a/internal/native/mock_server.go b/internal/native/mock_server.go index 38ab72d36..b32321ee0 100644 --- a/internal/native/mock_server.go +++ b/internal/native/mock_server.go @@ -11,7 +11,9 @@ import ( "encoding/json" "fmt" "log" + "net" "os" + "strconv" "strings" "unsafe" ) @@ -134,52 +136,6 @@ func (m *MockServer) WithSpecificationVersion(version specificationVersion) { C.pactffi_with_specification(m.pact.handle, C.int(version)) } -// CreateMockServer creates a new Mock Server from a given Pact file. -// Returns the port number it started on or an error if failed -func (m *MockServer) CreateMockServer(pact string, address string, tls bool) (int, error) { - log.Println("[DEBUG] mock server starting on address:", address) - cPact := C.CString(pact) - cAddress := C.CString(address) - defer free(cPact) - defer free(cAddress) - tlsEnabled := false - if tls { - tlsEnabled = true - } - - p := C.pactffi_create_mock_server(cPact, cAddress, C.bool(tlsEnabled)) - - // | Error | Description | - // |-------|-------------| - // | -1 | A null pointer was received | - // | -2 | The pact JSON could not be parsed | - // | -3 | The mock server could not be started | - // | -4 | The method panicked | - // | -5 | The address is not valid | - // | -6 | Could not create the TLS configuration with the self-signed certificate | - port := int(p) - switch port { - case -1: - return 0, ErrInvalidMockServerConfig - case -2: - return 0, ErrInvalidPact - case -3: - return 0, ErrMockServerUnableToStart - case -4: - return 0, ErrMockServerPanic - case -5: - return 0, ErrInvalidAddress - case -6: - return 0, ErrMockServerTLSConfiguration - default: - if port > 0 { - log.Println("[DEBUG] mock server running on port:", port) - return port, nil - } - return port, fmt.Errorf("an unknown error (code: %v) occurred when starting a mock server for the test", port) - } -} - // Verify verifies that all interactions were successful. If not, returns a slice // of Mismatch-es. Does not write the pact or cleanup server. func (m *MockServer) Verify(port int, dir string) (bool, []MismatchedRequest) { @@ -281,21 +237,36 @@ func libRustFree(str *C.char) { } // Start starts up the mock HTTP server on the given address:port and TLS config -// https://docs.rs/pact_mock_server_ffi/0.0.7/pact_mock_server_ffi/fn.create_mock_server_for_pact.html +// https://docs.rs/pact_ffi/latest/pact_ffi/mock_server/fn.pactffi_create_mock_server_for_transport.html func (m *MockServer) Start(address string, tls bool) (int, error) { if len(m.interactions) == 0 { return 0, ErrNoInteractions } log.Println("[DEBUG] mock server starting on address:", address) - cAddress := C.CString(address) - defer free(cAddress) - tlsEnabled := false + + host, portStr, err := net.SplitHostPort(address) + if err != nil { + return 0, ErrInvalidAddress + } + requestedPort, err := strconv.Atoi(portStr) + if err != nil { + return 0, ErrInvalidAddress + } + cHost := C.CString(host) + defer free(cHost) + var transport string if tls { - tlsEnabled = true + transport = "https" + } else { + transport = "http" } + cTransport := C.CString(transport) + defer free(cTransport) + + cConfig := (*C.char)(nil) - p := C.pactffi_create_mock_server_for_pact(m.pact.handle, cAddress, C.bool(tlsEnabled)) + msPort := int(C.pactffi_create_mock_server_for_transport(m.pact.handle, cHost, C.ushort(requestedPort), cTransport, cConfig)) // | Error | Description | // |-------|-------------| @@ -305,26 +276,23 @@ func (m *MockServer) Start(address string, tls bool) (int, error) { // | -4 | The method panicked | // | -5 | The address is not valid | // | -6 | Could not create the TLS configuration with the self-signed certificate | - port := int(p) - switch port { + switch msPort { case -1: return 0, ErrInvalidMockServerConfig case -2: - return 0, ErrInvalidPact + return 0, ErrInvalidMockServerConfig case -3: return 0, ErrMockServerUnableToStart case -4: return 0, ErrMockServerPanic case -5: return 0, ErrInvalidAddress - case -6: - return 0, ErrMockServerTLSConfiguration default: - if port > 0 { - log.Println("[DEBUG] mock server running on port:", port) - return port, nil + if msPort > 0 { + log.Println("[DEBUG] mock server running on port:", msPort) + return msPort, nil } - return port, fmt.Errorf("an unknown error (code: %v) occurred when starting a mock server for the test", port) + return msPort, fmt.Errorf("an unknown error (code: %v) occurred when starting a mock server for the test", msPort) } } @@ -412,7 +380,7 @@ func (m *MockServer) UsingPlugin(pluginName string, pluginVersion string) error return ErrHandleNotFound default: if res != 0 { - return fmt.Errorf("an unknown error (code: %v) occurred when adding a plugin for the test. Received error code:", res) + return fmt.Errorf("an unknown error (code: %v) occurred when adding a plugin for the test. Received error code", res) } } @@ -468,7 +436,7 @@ func (i *Interaction) WithPluginInteractionContents(part interactionPart, conten return ErrPluginSpecificError default: if res != 0 { - return fmt.Errorf("an unknown error (code: %v) occurred when adding a plugin for the test. Received error code:", res) + return fmt.Errorf("an unknown error (code: %v) occurred when adding a plugin for the test. Received error code", res) } } @@ -643,6 +611,22 @@ func (i *Interaction) WithStatus(status int) *Interaction { return i } +// WithReference records an external reference (e.g. a ticket or pull request) +// against the interaction. References are stored under comments.references[group][name] +// in the Pact file. This is a V4-only feature. +func (i *Interaction) WithReference(group, name, value string) *Interaction { + cGroup := C.CString(group) + defer free(cGroup) + cName := C.CString(name) + defer free(cName) + cValue := C.CString(value) + defer free(cValue) + + C.pactffi_add_interaction_reference(i.handle, cGroup, cName, cValue) + + return i +} + type stringLike interface { String() string } @@ -778,11 +762,11 @@ var ( // Log Errors var ( - ErrCantSetLogger = fmt.Errorf("can't set logger (applying the logger failed, perhaps because one is applied already).") - ErrNoLogger = fmt.Errorf("no logger has been initialized (call `logger_init` before any other log function).") - ErrSpecifierNotUtf8 = fmt.Errorf("The sink specifier was not UTF-8 encoded.") - ErrUnknownSinkType = fmt.Errorf(`the sink type specified is not a known type (known types: "buffer", "stdout", "stderr", or "file /some/path").`) - ErrMissingFilePath = fmt.Errorf("no file path was specified in a file-type sink specification.") - ErrCantOpenSinkToFile = fmt.Errorf("opening a sink to the specified file path failed (check permissions).") + ErrCantSetLogger = fmt.Errorf("can't set logger (applying the logger failed, perhaps because one is applied already)") + ErrNoLogger = fmt.Errorf("no logger has been initialized (call `logger_init` before any other log function)") + ErrSpecifierNotUtf8 = fmt.Errorf("the sink specifier was not UTF-8 encoded") + ErrUnknownSinkType = fmt.Errorf(`the sink type specified is not a known type (known types: "buffer", "stdout", "stderr", or "file /some/path")`) + ErrMissingFilePath = fmt.Errorf("no file path was specified in a file-type sink specification") + ErrCantOpenSinkToFile = fmt.Errorf("opening a sink to the specified file path failed (check permissions)") ErrCantConstructSink = fmt.Errorf("can't construct the log sink") ) diff --git a/internal/native/mock_server_test.go b/internal/native/mock_server_test.go index aa79a6373..7e53d8057 100644 --- a/internal/native/mock_server_test.go +++ b/internal/native/mock_server_test.go @@ -17,9 +17,34 @@ func init() { Init("") } +// newSimpleMockServer creates a mock server with a simple GET /foobar → 200 interaction +// using the programmatic API, replacing the removed CreateMockServer function. +func newSimpleMockServer(t *testing.T) (*MockServer, int) { + t.Helper() + m := NewHTTPPact("consumer", "provider") + m.NewInteraction("Some name for the test"). + UponReceiving("Some name for the test"). + Given("Some state"). + WithRequest("GET", "/foobar"). + WithStatus(200) + port, err := m.Start("0.0.0.0:0", false) + if err != nil { + t.Fatalf("failed to start mock server: %v", err) + } + return m, port +} + func TestMockServer_CreateAndCleanupMockServer(t *testing.T) { - m := MockServer{} - port, _ := m.CreateMockServer(pactComplex, "0.0.0.0:0", false) + m := NewHTTPPact("consumer", "provider") + m.NewInteraction("Some complex interaction"). + UponReceiving("Some complex interaction"). + Given("Some state"). + WithRequest("GET", "/foobar"). + WithStatus(200) + port, err := m.Start("0.0.0.0:0", false) + if err != nil { + t.Fatal("failed to start mock server:", err) + } defer m.CleanupMockServer(port) if port <= 0 { @@ -28,8 +53,7 @@ func TestMockServer_CreateAndCleanupMockServer(t *testing.T) { } func TestMockServer_MismatchesSuccess(t *testing.T) { - m := MockServer{} - port, _ := m.CreateMockServer(pactSimple, "0.0.0.0:0", false) + m, port := newSimpleMockServer(t) defer m.CleanupMockServer(port) res, err := http.Get(fmt.Sprintf("http://localhost:%d/foobar", port)) @@ -48,8 +72,7 @@ func TestMockServer_MismatchesSuccess(t *testing.T) { } func TestMockServer_MismatchesFail(t *testing.T) { - m := MockServer{} - port, _ := m.CreateMockServer(pactSimple, "0.0.0.0:0", false) + m, port := newSimpleMockServer(t) defer m.CleanupMockServer(port) mismatches := m.MockServerMismatchedRequests(port) @@ -62,8 +85,7 @@ func TestMockServer_VerifySuccess(t *testing.T) { tmpPactFolder, err := os.MkdirTemp("", "pact-go") assert.NoError(t, err) - m := MockServer{} - port, _ := m.CreateMockServer(pactSimple, "0.0.0.0:0", false) + m, port := newSimpleMockServer(t) defer m.CleanupMockServer(port) _, err = http.Get(fmt.Sprintf("http://localhost:%d/foobar", port)) @@ -84,8 +106,7 @@ func TestMockServer_VerifySuccess(t *testing.T) { func TestMockServer_VerifyFail(t *testing.T) { tmpPactFolder, err := os.MkdirTemp("", "pact-go") assert.NoError(t, err) - m := MockServer{} - port, _ := m.CreateMockServer(pactSimple, "0.0.0.0:0", false) + m, port := newSimpleMockServer(t) success, mismatches := m.Verify(port, tmpPactFolder) if success { @@ -101,8 +122,7 @@ func TestMockServer_WritePactfile(t *testing.T) { tmpPactFolder, err := os.MkdirTemp("", "pact-go") assert.NoError(t, err) - m := MockServer{} - port, _ := m.CreateMockServer(pactSimple, "0.0.0.0:0", false) + m, port := newSimpleMockServer(t) defer m.CleanupMockServer(port) _, err = http.Get(fmt.Sprintf("http://localhost:%d/foobar", port)) @@ -155,7 +175,6 @@ func TestHandleBasedHTTPTests(t *testing.T) { _, err = http.Get(fmt.Sprintf("http://0.0.0.0:%d/products", port)) assert.NoError(t, err) - mismatches := m.MockServerMismatchedRequests(port) if len(mismatches) != 0 { t.Fatalf("want 0 mismatches, got '%d'", len(mismatches)) @@ -223,73 +242,3 @@ func TestPluginInteraction(t *testing.T) { err = m.WritePactFile(port, tmpPactFolder) assert.NoError(t, err) } - -var pactSimple = `{ - "consumer": { - "name": "consumer" - }, - "provider": { - "name": "provider" - }, - "interactions": [ - { - "description": "Some name for the test", - "request": { - "method": "GET", - "path": "/foobar" - }, - "response": { - "status": 200 - }, - "description": "Some name for the test", - "provider_state": "Some state" - }] -}` - -var pactComplex = `{ - "consumer": { - "name": "consumer" - }, - "provider": { - "name": "provider" - }, - "interactions": [ - { - "request": { - "method": "GET", - "path": "/foobar", - "body": { - "pass": 1234, - "user": { - "address": "some address", - "name": "someusername", - "phone": 12345678, - "plaintext": "plaintext" - } - } - }, - "response": { - "status": 200 - }, - "description": "Some name for the test", - "provider_state": "Some state", - "matchingRules": { - "$.body.pass": { - "match": "regex", - "regex": "\\d+" - }, - "$.body.user.address": { - "match": "regex", - "regex": "\\s+" - }, - "$.body.user.name": { - "match": "regex", - "regex": "\\s+" - }, - "$.body.user.phone": { - "match": "regex", - "regex": "\\d+" - } - } - }] -}` diff --git a/internal/native/pact.h b/internal/native/pact.h index c5865d040..342a601e8 100644 --- a/internal/native/pact.h +++ b/internal/native/pact.h @@ -7,7 +7,7 @@ #ifndef pact_ffi_h #define pact_ffi_h -/* Generated with cbindgen:0.26.0 */ +/* Generated with cbindgen:0.29.2 */ /* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ @@ -657,18 +657,32 @@ int pactffi_get_error_message(char *buffer, /** * Convenience function to direct all logging to stdout. + * + * This function is equivalent to using [`pactffi_logger_init`] followed by the + * [`pactffi_logger_attach_sink`] with the appropriate sink specifier, and then + * [`pactffi_logger_apply`]. */ int pactffi_log_to_stdout(int level_filter); /** * Convenience function to direct all logging to stderr. + * + * This function is equivalent to using [`pactffi_logger_init`] followed by the + * [`pactffi_logger_attach_sink`] with the appropriate sink specifier, and then + * [`pactffi_logger_apply`]. */ int pactffi_log_to_stderr(int level_filter); /** * Convenience function to direct all logging to a file. * + * + * This function is equivalent to using [`pactffi_logger_init`] followed by the + * [`pactffi_logger_attach_sink`] with the appropriate sink specifier, and then + * [`pactffi_logger_apply`]. + * * # Safety + * * This function will fail if the file_name pointer is invalid or does not point to a NULL * terminated string. */ @@ -676,6 +690,13 @@ int pactffi_log_to_file(const char *file_name, int level_filter); /** * Convenience function to direct all logging to a task local memory buffer. + * + * This function is equivalent to using [`pactffi_logger_init`] followed by the + * [`pactffi_logger_attach_sink`] with the appropriate sink specifier, and then + * [`pactffi_logger_apply`]. + * + * The contents of the buffer can be fetched using + * [`pactffi_fetch_log_buffer`]. */ int pactffi_log_to_buffer(int level_filter); @@ -721,7 +742,7 @@ void pactffi_logger_init(void); * - `-1`: Can't set logger (applying the logger failed, perhaps because one is applied already). * - `-2`: No logger has been initialized (call `pactffi_logger_init` before any other log function). * - `-3`: The sink specifier was not UTF-8 encoded. - * - `-4`: The sink type specified is not a known type (known types: "stdout", "stderr", or "file /some/path"). + * - `-4`: The sink type specified is not a known type (known types: "stdout", "stderr", "buffer", or "file /some/path"). * - `-5`: No file path was specified in a file-type sink specification. * - `-6`: Opening a sink to the specified file path failed (check permissions). * @@ -740,6 +761,12 @@ int pactffi_logger_attach_sink(const char *sink_specifier, * * This function will install a global tracing subscriber. Any attempts to modify the logger * after the call to `logger_apply` will fail. + * + * # Error Handling + * + * The return error codes are as follows: + * + * - `-1`: Can't set logger (applying the logger failed, perhaps because one is applied already). */ int pactffi_logger_apply(void); @@ -793,7 +820,7 @@ struct PactInteractionIterator *pactffi_pact_model_interaction_iterator(struct P /** * Returns the Pact specification enum that the Pact is for. */ -int pactffi_pact_spec_version(const struct Pact *pact); +enum PactSpecification pactffi_pact_spec_version(const struct Pact *pact); /** * Frees the memory used by the Pact interaction model @@ -3089,35 +3116,6 @@ struct ProviderStateIterator *pactffi_sync_message_get_provider_state_iter(struc */ void pactffi_string_delete(char *string); -/** - * [DEPRECATED] External interface to create a HTTP mock server. A pointer to the pact JSON as a NULL-terminated C - * string is passed in, as well as the port for the mock server to run on. A value of 0 for the - * port will result in a port being allocated by the operating system. The port of the mock server is returned. - * - * * `pact_str` - Pact JSON - * * `addr_str` - Address to bind to in the form name:port (i.e. 127.0.0.1:0) - * * `tls` - boolean flag to indicate of the mock server should use TLS (using a self-signed certificate) - * - * This function is deprecated and replaced with `pactffi_create_mock_server_for_transport`. - * - * # Errors - * - * Errors are returned as negative values. - * - * | Error | Description | - * |-------|-------------| - * | -1 | A null pointer was received | - * | -2 | The pact JSON could not be parsed | - * | -3 | The mock server could not be started | - * | -4 | The method panicked | - * | -5 | The address is not valid | - * | -6 | Could not create the TLS configuration with the self-signed certificate | - * - */ -int32_t pactffi_create_mock_server(const char *pact_str, - const char *addr_str, - bool tls); - /** * Fetch the CA Certificate used to generate the self-signed certificate for the TLS mock server. * @@ -3130,34 +3128,6 @@ int32_t pactffi_create_mock_server(const char *pact_str, */ char *pactffi_get_tls_ca_certificate(void); -/** - * [DEPRECATED] External interface to create a HTTP mock server. A Pact handle is passed in, - * as well as the port for the mock server to run on. A value of 0 for the port will result in a - * port being allocated by the operating system. The port of the mock server is returned. - * - * * `pact` - Handle to a Pact model created with created with `pactffi_new_pact`. - * * `addr_str` - Address to bind to in the form name:port (i.e. 127.0.0.1:0). Must be a valid UTF-8 NULL-terminated string. - * * `tls` - boolean flag to indicate of the mock server should use TLS (using a self-signed certificate) - * - * This function is deprecated and replaced with `pactffi_create_mock_server_for_transport`. - * - * # Errors - * - * Errors are returned as negative values. - * - * | Error | Description | - * |-------|-------------| - * | -1 | An invalid handle was received. Handles should be created with `pactffi_new_pact` | - * | -3 | The mock server could not be started | - * | -4 | The method panicked | - * | -5 | The address is not valid | - * | -6 | Could not create the TLS configuration with the self-signed certificate | - * - */ -int32_t pactffi_create_mock_server_for_pact(PactHandle pact, - const char *addr_str, - bool tls); - /** * Create a mock server for the provided Pact handle and transport. If the transport is not * provided (it is a NULL pointer or an empty string), will default to an HTTP transport. The @@ -3548,11 +3518,12 @@ bool pactffi_with_specification(PactHandle pact, int pactffi_handle_get_pact_spec_version(PactHandle pact); /** - * Sets the additional metadata on the Pact file. Common uses are to add the client library details such as the name and version - * Returns false if the interaction or Pact can't be modified (i.e. the mock server for it has already started) + * Sets the additional metadata on the Pact file. Common uses are to add the client library + * details such as the name and version. Returns false if the interaction or Pact can't be + * modified (i.e. the mock server for it has already started) or the namespace is readonly. * * * `pact` - Handle to a Pact model - * * `namespace` - the top level metadat key to set any key values on + * * `namespace` - the top level metadata key to set any key values on * * `name` - the key to set * * `value` - the value to set */ @@ -3725,30 +3696,43 @@ bool pactffi_response_status_v2(InteractionHandle interaction, const char *status); /** - * Adds the body for the interaction. Returns false if the interaction or Pact can't be - * modified (i.e. the mock server for it has already started) + * Adds the body for the interaction. Returns false if the interaction or Pact + * can't be modified (i.e. the mock server for it has already started) * - * * `part` - The part of the interaction to add the body to (Request or Response). - * * `content_type` - The content type of the body. Defaults to `text/plain`. Will be ignored if a content type - * header is already set. - * * `body` - The body contents. For JSON payloads, matching rules can be embedded in the body. See - * [IntegrationJson.md](https://github.com/pact-foundation/pact-reference/blob/master/rust/pact_ffi/IntegrationJson.md) + * * `part` - The part of the interaction to add the body to (Request or + * Response). This is ignored for asynchronous message interactions. + * * `content_type` - The content type of the body, or `NULL` to use the + * internal logic. + * * `body` - The body contents. For JSON payloads, matching rules can be + * embedded in the body. See + * [IntegrationJson.md](https://github.com/pact-foundation/pact-reference/blob/master/rust/pact_ffi/IntegrationJson.md) * - * For HTTP and async message interactions, this will overwrite the body. With asynchronous messages, the - * part parameter will be ignored. With synchronous messages, the request contents will be overwritten, - * while a new response will be appended to the message. + * The payload's content type is determined as follows, whichever is first: + * + * - The `content_type` argument to this function if provided. If the provided + * value fails to parse, and error is logged and it will be ignored. + * - The `Content-Type` header for HTTP interaction, or `contentType` metadata + * entry for message interactions. + * - From automatic detection of the body contents. + * - Defaults to `text/plain` as a last resort. + * + * Furthermore, the `Content-Type` header or `contentType` metadata entry will + * be updated with the above determined content type, _unless_ it is already + * set. + * + * This function will overwrite the body contents if they exist, with the + * exception of the response part of synchronous message interactions, where a + * new response will be appended. * * # Safety * - * The interaction contents and content type must either be NULL pointers, or point to valid - * UTF-8 encoded NULL-terminated strings. Otherwise, behaviour is undefined. + * The interaction contents and content type must either be NULL pointers, or + * point to valid UTF-8 encoded NULL-terminated strings. Otherwise, behaviour + * is undefined. * * # Error Handling * - * If the contents is a NULL pointer, it will set the body contents as null. If the content - * type is a null pointer, or can't be parsed, it will set the content type as TEXT. - * Returns false if the interaction or Pact can't be modified (i.e. the mock server for it has - * already started) or an error has occurred. + * If the contents is a NULL pointer, it will set the body contents as null. */ bool pactffi_with_body(InteractionHandle interaction, int part, @@ -3792,6 +3776,14 @@ bool pactffi_with_binary_body(InteractionHandle interaction, size_t size); /** + *