diff --git a/testing/codegen/clients_test.go b/testing/codegen/clients_test.go index 28ea061b..a7044bb1 100644 --- a/testing/codegen/clients_test.go +++ b/testing/codegen/clients_test.go @@ -15,6 +15,13 @@ func TestGenerateClient(t *testing.T) { Code map[string][]string Path string }{ + "with-payload": { + DSL: testdata.WithPayloadDSL, + Code: map[string][]string{ + "client-methods": {testdata.ClientMethodsWithPayloadCode}, + }, + Path: "gen/with_payload_service/with_payload_servicetest/client.go", + }, "with-result": { DSL: testdata.WithResultDSL, Code: map[string][]string{ @@ -22,12 +29,12 @@ func TestGenerateClient(t *testing.T) { }, Path: "gen/with_result_service/with_result_servicetest/client.go", }, - "without-result": { - DSL: testdata.WithoutResultDSL, + "without-payload-result": { + DSL: testdata.WithoutPayloadResultDSL, Code: map[string][]string{ - "client-methods": {testdata.ClientMethodsWithoutResultCode}, + "client-methods": {testdata.ClientMethodsWithoutPayloadResultCode}, }, - Path: "gen/without_result_service/without_result_servicetest/client.go", + Path: "gen/without_payload_result_service/without_payload_result_servicetest/client.go", }, } for name, c := range cases { diff --git a/testing/codegen/harness.go b/testing/codegen/harness.go index 018be4c5..17364e5a 100644 --- a/testing/codegen/harness.go +++ b/testing/codegen/harness.go @@ -263,3 +263,13 @@ func hasMethodJSONRPC(root *expr.RootExpr, svc *expr.ServiceExpr, m *expr.Method } return false } + +// hasPayloads checks if the service has methods with payloads. +func hasPayloads(svc *expr.ServiceExpr) bool { + for _, m := range svc.Methods { + if m.Payload.Type != expr.Empty || m.StreamingPayload.Type != expr.Empty { + return true + } + } + return false +} diff --git a/testing/codegen/scenarios_test.go b/testing/codegen/scenarios_test.go index 2c8a54ae..ffed302d 100644 --- a/testing/codegen/scenarios_test.go +++ b/testing/codegen/scenarios_test.go @@ -15,6 +15,13 @@ func TestGenerateScenarios(t *testing.T) { Code map[string][]string Path string }{ + "with-payload": { + DSL: testdata.WithPayloadDSL, + Code: map[string][]string{ + "scenario-runner": {testdata.ScenarioRunnerWithPayloadCode}, + }, + Path: "gen/with_payload_service/with_payload_servicetest/scenarios.go", + }, "with-result": { DSL: testdata.WithResultDSL, Code: map[string][]string{ @@ -22,12 +29,12 @@ func TestGenerateScenarios(t *testing.T) { }, Path: "gen/with_result_service/with_result_servicetest/scenarios.go", }, - "without-result": { - DSL: testdata.WithoutResultDSL, + "without-payload-result": { + DSL: testdata.WithoutPayloadResultDSL, Code: map[string][]string{ - "scenario-runner": {testdata.ScenarioRunnerWithoutResultCode}, + "scenario-runner": {testdata.ScenarioRunnerWithoutPayloadResultCode}, }, - Path: "gen/without_result_service/without_result_servicetest/scenarios.go", + Path: "gen/without_payload_result_service/without_payload_result_servicetest/scenarios.go", }, } for name, c := range cases { diff --git a/testing/codegen/suite.go b/testing/codegen/suite.go index 00a4751a..b474204a 100644 --- a/testing/codegen/suite.go +++ b/testing/codegen/suite.go @@ -12,19 +12,20 @@ import ( type ( // suiteData contains data for generating test suite. suiteData struct { - Package string - Service *service.Data - ImportPath string - TestPkg string // Package alias for the test harness - NonStream []*suiteMethodData - Stream []*suiteMethodData - HasErrors bool - HasStreams bool - HasHTTP bool - HasGRPC bool - HasJSONRPC bool - UseTD bool - UseCtx bool + Package string + Service *service.Data + ImportPath string + TestPkg string // Package alias for the test harness + NonStream []*suiteMethodData + Stream []*suiteMethodData + HasErrors bool + HasStreams bool + HasHTTP bool + HasGRPC bool + HasJSONRPC bool + HasPayloads bool + UseTD bool + UseCtx bool } // suiteTarget describes one transport/mode target to exercise for a method. @@ -64,7 +65,7 @@ func generateSuiteTopLevel(genpkg string, examplePkg string, root *expr.RootExpr {Path: "testing"}, {Path: "time"}, {Path: filepath.Join(genpkg, data.Service.PathName), Name: data.Service.PkgName}, - {Path: filepath.Join(genpkg, data.Service.PathName, data.Service.PathName+"test"), Name: data.Service.VarName+"test"}, + {Path: filepath.Join(genpkg, data.Service.PathName, data.Service.PathName+"test"), Name: data.Service.VarName + "test"}, } sections := []*codegen.SectionTemplate{ // Use empty title to generate header without "DO NOT EDIT" comment, like Goa examples @@ -89,17 +90,18 @@ func generateSuiteTopLevel(genpkg string, examplePkg string, root *expr.RootExpr func buildSuiteData(genpkg, pkg string, root *expr.RootExpr, svc *expr.ServiceExpr) *suiteData { sd := service.NewServicesData(root).Get(svc.Name) data := &suiteData{ - Package: pkg, - Service: sd, - ImportPath: filepath.Join(genpkg, sd.PathName), - TestPkg: sd.VarName + "test", - NonStream: make([]*suiteMethodData, 0, len(svc.Methods)), - Stream: make([]*suiteMethodData, 0, len(svc.Methods)), - HasErrors: hasErrors(svc), - HasStreams: hasStreams(svc), - HasHTTP: hasHTTPTransport(root, svc), - HasGRPC: hasGRPCTransport(root, svc), - HasJSONRPC: hasJSONRPCTransport(root, svc), + Package: pkg, + Service: sd, + ImportPath: filepath.Join(genpkg, sd.PathName), + TestPkg: sd.VarName + "test", + NonStream: make([]*suiteMethodData, 0, len(svc.Methods)), + Stream: make([]*suiteMethodData, 0, len(svc.Methods)), + HasErrors: hasErrors(svc), + HasStreams: hasStreams(svc), + HasHTTP: hasHTTPTransport(root, svc), + HasGRPC: hasGRPCTransport(root, svc), + HasJSONRPC: hasJSONRPCTransport(root, svc), + HasPayloads: hasPayloads(svc), } for _, m := range svc.Methods { md := &suiteMethodData{Method: sd.Method(m.Name)} diff --git a/testing/codegen/suite_test.go b/testing/codegen/suite_test.go index 55281e70..4cb52b12 100644 --- a/testing/codegen/suite_test.go +++ b/testing/codegen/suite_test.go @@ -14,6 +14,13 @@ func TestGenerateSuiteTopLevel(t *testing.T) { Code map[string][]string Path string }{ + "with-payload": { + DSL: testdata.WithPayloadDSL, + Code: map[string][]string{ + "suite-test": {testdata.SuiteTestWithPayloadCode}, + }, + Path: "with_payload_service_suite_test.go", + }, "with-result": { DSL: testdata.WithResultDSL, Code: map[string][]string{ @@ -21,12 +28,12 @@ func TestGenerateSuiteTopLevel(t *testing.T) { }, Path: "with_result_service_suite_test.go", }, - "without-result": { - DSL: testdata.WithoutResultDSL, + "without-payload-result": { + DSL: testdata.WithoutPayloadResultDSL, Code: map[string][]string{ - "suite-test": {testdata.SuiteTestWithoutResultCode}, + "suite-test": {testdata.SuiteTestWithoutPayloadResultCode}, }, - Path: "without_result_service_suite_test.go", + Path: "without_payload_result_service_suite_test.go", }, } for name, c := range cases { diff --git a/testing/codegen/templates/suite_test.go.tpl b/testing/codegen/templates/suite_test.go.tpl index 45aadb9c..17a6a790 100644 --- a/testing/codegen/templates/suite_test.go.tpl +++ b/testing/codegen/templates/suite_test.go.tpl @@ -10,7 +10,9 @@ func Run{{ .Service.StructName }}Harness(t *testing.T, svc {{ .Service.PkgName } h := {{ .TestPkg }}.NewHarness(t, svc) defer h.Close() +{{ if .HasPayloads }} td := {{ .TestPkg }}.NewTestData() +{{- end }} {{- range .NonStream }} {{- $m := . }} diff --git a/testing/codegen/testdata/code.go b/testing/codegen/testdata/code.go index 29d36b53..70577be3 100644 --- a/testing/codegen/testdata/code.go +++ b/testing/codegen/testdata/code.go @@ -1,5 +1,49 @@ package testdata +var ClientMethodsWithPayloadCode = `// WithPayloadMethod calls the WithPayloadMethod method using the configured +// transport. +func (c *Client) WithPayloadMethod(ctx context.Context, p *withpayloadservice.WithPayloadMethodPayload) error { + // Determine which transport to use + transport := c.transport + if transport == AutoTransport { + // Use the first available transport + if c.httpClient != nil { + transport = HTTPTransport + } else if c.grpcClient != nil { + transport = GRPCTransport + } else if c.jsonrpcClient != nil { + transport = JSONRPCTransport + } + } + + switch transport { + case HTTPTransport: + if c.httpClient == nil { + return fmt.Errorf("HTTP transport not configured") + } + endpoint := c.httpClient.WithPayloadMethod() + _, err := endpoint(ctx, p) + return err + case GRPCTransport: + if c.grpcClient == nil { + return fmt.Errorf("gRPC transport not configured") + } + endpoint := c.grpcClient.WithPayloadMethod() + _, err := endpoint(ctx, p) + return err + case JSONRPCTransport: + if c.jsonrpcClient == nil { + return fmt.Errorf("JSON-RPC transport not configured") + } + endpoint := c.jsonrpcClient.WithPayloadMethod() + _, err := endpoint(ctx, p) + return err + + default: + return fmt.Errorf("no transport available for WithPayloadMethod") + } +} +` var ClientMethodsWithResultCode = `// WithResultMethod calls the WithResultMethod method using the configured // transport. func (c *Client) WithResultMethod(ctx context.Context) (*withresultservice.WithResultMethodResult, error) { @@ -54,9 +98,9 @@ func (c *Client) WithResultMethod(ctx context.Context) (*withresultservice.WithR } ` -var ClientMethodsWithoutResultCode = `// WithoutResultMethod calls the WithoutResultMethod method using the -// configured transport. -func (c *Client) WithoutResultMethod(ctx context.Context) error { +var ClientMethodsWithoutPayloadResultCode = `// WithoutPayloadResultMethod calls the WithoutPayloadResultMethod method using +// the configured transport. +func (c *Client) WithoutPayloadResultMethod(ctx context.Context) error { // Determine which transport to use transport := c.transport if transport == AutoTransport { @@ -75,26 +119,26 @@ func (c *Client) WithoutResultMethod(ctx context.Context) error { if c.httpClient == nil { return fmt.Errorf("HTTP transport not configured") } - endpoint := c.httpClient.WithoutResultMethod() + endpoint := c.httpClient.WithoutPayloadResultMethod() _, err := endpoint(ctx, nil) return err case GRPCTransport: if c.grpcClient == nil { return fmt.Errorf("gRPC transport not configured") } - endpoint := c.grpcClient.WithoutResultMethod() + endpoint := c.grpcClient.WithoutPayloadResultMethod() _, err := endpoint(ctx, nil) return err case JSONRPCTransport: if c.jsonrpcClient == nil { return fmt.Errorf("JSON-RPC transport not configured") } - endpoint := c.jsonrpcClient.WithoutResultMethod() + endpoint := c.jsonrpcClient.WithoutPayloadResultMethod() _, err := endpoint(ctx, nil) return err default: - return fmt.Errorf("no transport available for WithoutResultMethod") + return fmt.Errorf("no transport available for WithoutPayloadResultMethod") } } ` @@ -335,6 +379,299 @@ func (h *Harness) HTTPDo(req *http.Request) *http.Response { } ` +var ScenarioRunnerWithPayloadCode = `// ScenarioRunner executes test scenarios. +type ScenarioRunner struct { + scenarios []Scenario + validators Validators // Global validator configuration +} + +// LoadScenarios loads scenarios from a YAML file. +func LoadScenarios(path string) (*ScenarioRunner, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("failed to read scenarios file: %w", err) + } + + var config ScenarioConfig + if err := yaml.Unmarshal(data, &config); err != nil { + return nil, fmt.Errorf("failed to parse scenarios YAML: %w", err) + } + + return &ScenarioRunner{ + scenarios: config.Scenarios, + validators: config.Validators, + }, nil +} + +// NewScenarioRunner creates a new scenario runner. +func NewScenarioRunner() *ScenarioRunner { + return &ScenarioRunner{ + scenarios: make([]Scenario, 0), + } +} + +// AddScenario adds a scenario to the runner. +func (r *ScenarioRunner) AddScenario(scenario Scenario) { + r.scenarios = append(r.scenarios, scenario) +} + +// Run executes all scenarios. +func (r *ScenarioRunner) Run(t *testing.T, client *Client) { + if r == nil { + t.Fatal("ScenarioRunner is nil") + } + if client == nil { + t.Fatal("Client is nil") + } + for _, scenario := range r.scenarios { + t.Run(scenario.Name, func(t *testing.T) { + r.runScenario(t, client, scenario) + }) + } +} + +// RunNamed executes a specific scenario by name. +func (r *ScenarioRunner) RunNamed(t *testing.T, client *Client, name string) { + if r == nil { + t.Fatal("ScenarioRunner is nil") + } + if client == nil { + t.Fatal("Client is nil") + } + if name == "" { + t.Fatal("scenario name is empty") + } + for _, scenario := range r.scenarios { + if scenario.Name == name { + r.runScenario(t, client, scenario) + return + } + } + t.Fatalf("scenario %q not found", name) +} + +func (r *ScenarioRunner) runScenario(t *testing.T, client *Client, scenario Scenario) { + // Apply default transport if specified + if scenario.Transport != "" { + client = r.selectTransport(client, scenario.Transport) + } + + for i, step := range scenario.Steps { + t.Run(fmt.Sprintf("step_%d_%s", i+1, step.Method), func(t *testing.T) { + // Apply scenario-level timeout if step doesn't override + if step.Timeout == "" && scenario.Timeout != "" { + step.Timeout = scenario.Timeout + } + r.runStep(t, client, step) + }) + } +} + +func (r *ScenarioRunner) runStep(t *testing.T, client *Client, step Step) { + // Apply per-step transport override + if step.Transport != "" { + client = r.selectTransport(client, step.Transport) + } + + // Validate transport availability + if step.Transport != "" && step.Transport != "auto" { + if transports, ok := TransportAvailability[step.Method]; ok { + found := false + for _, t := range transports { + if t == step.Transport { + found = true + break + } + } + if !found { + t.Fatalf("method %q does not support transport %q, available: %v", + step.Method, step.Transport, transports) + } + } + } + + // Process payload + payload := step.Payload + ctx := context.Background() + + // Apply timeout if specified + if step.Timeout != "" { + duration, err := time.ParseDuration(step.Timeout) + if err != nil { + t.Fatalf("invalid timeout %q: %v", step.Timeout, err) + } + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, duration) + defer cancel() + } + + // Execute the method + result, err := r.executeMethod(ctx, client, step.Method, payload) + + // Handle error expectation + if step.Expect.Error != "" { + if err == nil { + t.Errorf("expected error %q but got none", step.Expect.Error) + } else if !strings.Contains(err.Error(), step.Expect.Error) { + t.Errorf("expected error containing %q but got %q", step.Expect.Error, err.Error()) + } + return + } + + // Handle unexpected error + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + // Validate result if expected + if step.Expect.Result != nil || step.Expect.Validator != "" { + r.validateResult(t, step.Method, result, step.Expect) + } + + // Handle streaming expectations + if len(step.Expect.Stream) > 0 { + r.validateStream(t, step.Method, result, step.Expect) + } +} + +func (r *ScenarioRunner) executeMethod(ctx context.Context, client *Client, method string, payload map[string]any) (any, error) { + switch method { + case "WithPayloadMethod": + // Convert payload map to typed payload + p := &withpayloadservice.WithPayloadMethodPayload{} + if err := r.mapToStruct(payload, p); err != nil { + return nil, fmt.Errorf("invalid payload for WithPayloadMethod: %w", err) + } + return nil, client.WithPayloadMethod(ctx, p) + default: + return nil, fmt.Errorf("unknown method: %s", method) + } +} + +func (r *ScenarioRunner) mapToStruct(data map[string]any, target any) error { + if data == nil { + // nil data is okay, just return without setting anything + return nil + } + if target == nil { + return fmt.Errorf("target is nil") + } + // Convert map to JSON then unmarshal to struct + jsonData, err := json.Marshal(data) + if err != nil { + return err + } + return json.Unmarshal(jsonData, target) +} + +func (r *ScenarioRunner) validateResult(t *testing.T, method string, result any, expect Expectation) { + if result == nil && expect.Result == nil && expect.Validator == "" { + // Nothing to validate + return + } + + // If custom validator specified in YAML, call it + if expect.Validator != "" { + // Call the user-defined validator function + // The function signature should be: func(t *testing.T, result *ServiceType, expected map[string]any) + r.callValidator(t, method, result, expect) + return + } + + // Fall back to default validation + if expect.Result != nil { + if err := defaultValidateResult(result, expect.Result); err != nil { + t.Errorf("validation failed for %s: %v", method, err) + } + } +} + +// callValidator calls the user-specified validator function. +// The validator function must be defined in the test package. +func (r *ScenarioRunner) callValidator(t *testing.T, method string, result any, expect Expectation) { + // For each validator found in YAML, we generate a direct call + // Users must define these functions in their test files + + validatorName := expect.Validator + _ = validatorName // avoid unused variable in case no validators are defined + + switch method { + case "WithPayloadMethod": + t.Errorf("method %q has no result to validate", method) + default: + t.Errorf("unknown method: %s", method) + } +} + +// defaultValidateResult provides basic equality checking for results. +func defaultValidateResult(result any, expected map[string]any) error { + if result == nil && len(expected) > 0 { + return fmt.Errorf("expected result but got nil") + } + + if result == nil && len(expected) == 0 { + return nil // Both nil, considered equal + } + + // Convert result to map for comparison + resultMap := make(map[string]any) + resultJSON, err := json.Marshal(result) + if err != nil { + return fmt.Errorf("failed to marshal result: %w", err) + } + if err := json.Unmarshal(resultJSON, &resultMap); err != nil { + return fmt.Errorf("failed to unmarshal result to map: %w", err) + } + + // Compare each expected field + for key, expectedValue := range expected { + actualValue, ok := resultMap[key] + if !ok { + return fmt.Errorf("missing expected field %q", key) + } + + // Convert both to JSON for deep comparison + expectedJSON, _ := json.Marshal(expectedValue) + actualJSON, _ := json.Marshal(actualValue) + if string(expectedJSON) != string(actualJSON) { + return fmt.Errorf("field %q: expected %s, got %s", key, expectedJSON, actualJSON) + } + } + + return nil +} + +func (r *ScenarioRunner) validateStream(t *testing.T, method string, stream any, expect Expectation) { + if stream == nil { + t.Errorf("stream is nil for method %s", method) + return + } + + // Stream validation with custom validators + if expect.Validator != "" { + t.Logf("Stream validator %s specified for %s - implement stream validation", expect.Validator, method) + return + } + + // No default stream validation - streams are too varied + t.Logf("Stream validation for %s: specify a validator in YAML or implement custom validation", method) +} + +func (r *ScenarioRunner) selectTransport(client *Client, transport string) *Client { + switch transport { + case "http", "http-sse", "http-ws": + return client.HTTP() + case "grpc": + return client.GRPC() + case "jsonrpc", "jsonrpc-sse", "jsonrpc-ws": + return client.JSONRPC() + default: + return client // auto or unknown - use default + } +} +` + var ScenarioRunnerWithResultCode = `// ScenarioRunner executes test scenarios. type ScenarioRunner struct { scenarios []Scenario @@ -625,7 +962,7 @@ func (r *ScenarioRunner) selectTransport(client *Client, transport string) *Clie } ` -var ScenarioRunnerWithoutResultCode = `// ScenarioRunner executes test scenarios. +var ScenarioRunnerWithoutPayloadResultCode = `// ScenarioRunner executes test scenarios. type ScenarioRunner struct { scenarios []Scenario validators Validators // Global validator configuration @@ -783,8 +1120,8 @@ func (r *ScenarioRunner) runStep(t *testing.T, client *Client, step Step) { func (r *ScenarioRunner) executeMethod(ctx context.Context, client *Client, method string, payload map[string]any) (any, error) { switch method { - case "WithoutResultMethod": - return nil, client.WithoutResultMethod(ctx) + case "WithoutPayloadResultMethod": + return nil, client.WithoutPayloadResultMethod(ctx) default: return nil, fmt.Errorf("unknown method: %s", method) } @@ -838,7 +1175,7 @@ func (r *ScenarioRunner) callValidator(t *testing.T, method string, result any, _ = validatorName // avoid unused variable in case no validators are defined switch method { - case "WithoutResultMethod": + case "WithoutPayloadResultMethod": t.Errorf("method %q has no result to validate", method) default: t.Errorf("unknown method: %s", method) @@ -913,6 +1250,27 @@ func (r *ScenarioRunner) selectTransport(client *Client, transport string) *Clie } ` +var SuiteTestWithPayloadCode = `// RunWithPayloadServiceHarness exercises the generated harness against your +// service implementation. +// Call this helper from your test, passing your service implementation. +func RunWithPayloadServiceHarness(t *testing.T, svc withpayloadservice.Service) { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + h := withPayloadServicetest.NewHarness(t, svc) + defer h.Close() + + td := withPayloadServicetest.NewTestData() + t.Run("WithPayloadMethod", func(t *testing.T) { + err := h.Client.WithPayloadMethod(ctx, td.ValidWithPayloadMethodPayload()) + if err != nil { + t.Errorf("WithPayloadMethod failed: %v", err) + } + }) +} +` + var SuiteTestWithResultCode = `// RunWithResultServiceHarness exercises the generated harness against your // service implementation. // Call this helper from your test, passing your service implementation. @@ -924,7 +1282,6 @@ func RunWithResultServiceHarness(t *testing.T, svc withresultservice.Service) { h := withResultServicetest.NewHarness(t, svc) defer h.Close() - td := withResultServicetest.NewTestData() t.Run("WithResultMethod", func(t *testing.T) { result, err := h.Client.WithResultMethod(ctx) if err != nil { @@ -937,22 +1294,21 @@ func RunWithResultServiceHarness(t *testing.T, svc withresultservice.Service) { } ` -var SuiteTestWithoutResultCode = `// RunWithoutResultServiceHarness exercises the generated harness against your -// service implementation. +var SuiteTestWithoutPayloadResultCode = `// RunWithoutPayloadResultServiceHarness exercises the generated harness +// against your service implementation. // Call this helper from your test, passing your service implementation. -func RunWithoutResultServiceHarness(t *testing.T, svc withoutresultservice.Service) { +func RunWithoutPayloadResultServiceHarness(t *testing.T, svc withoutpayloadresultservice.Service) { t.Helper() ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - h := withoutResultServicetest.NewHarness(t, svc) + h := withoutPayloadResultServicetest.NewHarness(t, svc) defer h.Close() - td := withoutResultServicetest.NewTestData() - t.Run("WithoutResultMethod", func(t *testing.T) { - err := h.Client.WithoutResultMethod(ctx) + t.Run("WithoutPayloadResultMethod", func(t *testing.T) { + err := h.Client.WithoutPayloadResultMethod(ctx) if err != nil { - t.Errorf("WithoutResultMethod failed: %v", err) + t.Errorf("WithoutPayloadResultMethod failed: %v", err) } }) } diff --git a/testing/codegen/testdata/dsls.go b/testing/codegen/testdata/dsls.go index a2e58f45..da0514ce 100644 --- a/testing/codegen/testdata/dsls.go +++ b/testing/codegen/testdata/dsls.go @@ -4,6 +4,21 @@ import ( . "goa.design/goa/v3/dsl" ) +var WithPayloadDSL = func() { + Service("WithPayloadService", func() { + Method("WithPayloadMethod", func() { + Payload(func() { + Field(1, "Attribute", String) + }) + HTTP(func() { + GET("/") + }) + GRPC(func() {}) + JSONRPC(func() {}) + }) + }) +} + var WithResultDSL = func() { Service("WithResultService", func() { Method("WithResultMethod", func() { @@ -19,9 +34,9 @@ var WithResultDSL = func() { }) } -var WithoutResultDSL = func() { - Service("WithoutResultService", func() { - Method("WithoutResultMethod", func() { +var WithoutPayloadResultDSL = func() { + Service("WithoutPayloadResultService", func() { + Method("WithoutPayloadResultMethod", func() { HTTP(func() { GET("/") })