From ccbedc032916b10cf6e0dee8a1021aa9d164ee6e Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 19 May 2026 17:32:16 -0400 Subject: [PATCH] chore(deployments): cover OpenAPI operation tag ingestion with integration test https://linear.app/speakeasy/issue/AGE-2347/openapi-source-tag-ingestion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parser → DB pipeline for OpenAPI operation tags was already wired end-to-end but had no test asserting that tags actually persist. Adds an integration test using a new minimal fixture that exercises multi, single, and zero operation-level tags, plus the root-level document tags case to confirm those do not bleed onto untagged operations. --- .../deployments/createdeployment_test.go | 68 ++++++++++++++++++ .../deployments/fixtures/todo-with-tags.yaml | 71 +++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 server/internal/deployments/fixtures/todo-with-tags.yaml diff --git a/server/internal/deployments/createdeployment_test.go b/server/internal/deployments/createdeployment_test.go index fb74ea92c0..7f1cb888d7 100644 --- a/server/internal/deployments/createdeployment_test.go +++ b/server/internal/deployments/createdeployment_test.go @@ -107,6 +107,74 @@ func TestDeploymentsService_CreateDeployment(t *testing.T) { }) } +func TestDeploymentsService_CreateDeployment_PersistsOperationTags(t *testing.T) { + t.Parallel() + + assetStorage := assetstest.NewTestBlobStore(t) + + ctx, ti := newTestDeploymentService(t, assetStorage) + + bs := bytes.NewBuffer(testenv.ReadFixture(t, "fixtures/todo-with-tags.yaml")) + + ares, err := ti.assets.UploadOpenAPIv3(ctx, &agen.UploadOpenAPIv3Form{ + ApikeyToken: nil, + SessionToken: nil, + ProjectSlugInput: nil, + ContentType: "application/x-yaml", + ContentLength: int64(bs.Len()), + }, io.NopCloser(bs)) + require.NoError(t, err, "upload openapi v3 asset") + + dep, err := ti.service.CreateDeployment(ctx, &gen.CreateDeploymentPayload{ + IdempotencyKey: "test-tagged-deployment", + Openapiv3Assets: []*gen.AddOpenAPIv3DeploymentAssetForm{ + { + AssetID: ares.Asset.ID, + Name: "test-doc", + Slug: "test-doc", + }, + }, + Functions: []*gen.AddFunctionsForm{}, + Packages: []*gen.AddDeploymentPackageForm{}, + ApikeyToken: nil, + SessionToken: nil, + ProjectSlugInput: nil, + GithubRepo: nil, + GithubPr: nil, + GithubSha: nil, + ExternalID: nil, + ExternalURL: nil, + }) + require.NoError(t, err, "create deployment") + require.Equal(t, "completed", dep.Deployment.Status, "deployment status is not completed") + + repo := testrepo.New(ti.conn) + tools, err := repo.ListDeploymentHTTPTools(ctx, uuid.MustParse(dep.Deployment.ID)) + require.NoError(t, err, "list deployment tools") + require.Len(t, tools, 3, "expected 3 tools") + + tagsByName := make(map[string][]string, len(tools)) + for _, tool := range tools { + tagsByName[tool.Name] = tool.Tags + } + + // Operation with multiple tags persists every tag in source order. + require.Equal(t, []string{"todos", "admin"}, tagsByName["test_doc_get_todos"], + "multi-tagged operation should persist both tags in source order") + + // Operation with a single tag persists that one tag. + require.Equal(t, []string{"todos"}, tagsByName["test_doc_get_todo_by_id"], + "single-tagged operation should persist its one tag") + + // Operation without tags must remain untagged even though the document + // declares root-level tag metadata. The column is NOT NULL, so the value + // should be an empty slice, not nil. + require.NotNil(t, tagsByName["test_doc_delete_todo"], + "untagged operation should receive a non-nil empty slice (the column is NOT NULL)") + require.Empty(t, tagsByName["test_doc_delete_todo"], + "root-level document tags must not propagate onto an operation that declares no tags") +} + func TestDeploymentsService_CreateDeployment_NonBlocking(t *testing.T) { t.Parallel() diff --git a/server/internal/deployments/fixtures/todo-with-tags.yaml b/server/internal/deployments/fixtures/todo-with-tags.yaml new file mode 100644 index 0000000000..d802be982f --- /dev/null +++ b/server/internal/deployments/fixtures/todo-with-tags.yaml @@ -0,0 +1,71 @@ +openapi: 3.1.0 +info: + title: Todo API (tagged) + description: A minimal tagged variant used to verify OpenAPI operation tags are persisted into http_tool_definitions.tags during deployment ingestion. + version: 1.0.0 + +servers: + - url: https://api.example.com/v1 + description: Production server + +# Root-level tag declarations are metadata for documentation tooling — they +# must NOT propagate onto an operation that declares no tags of its own. +tags: + - name: todos + description: Todo endpoints. + - name: admin + description: Admin endpoints. + +paths: + /todos: + get: + summary: Get all todos + operationId: getTodos + tags: + - todos + - admin + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + todos: + type: array + items: + type: object + /todos/{id}: + get: + summary: Get a todo by ID + operationId: getTodoById + tags: + - todos + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + delete: + summary: Delete a todo + operationId: deleteTodo + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + responses: + '204': + description: Todo deleted successfully