From 953e10c3c273d91053d16d2417e956ffaa578c63 Mon Sep 17 00:00:00 2001 From: Daniel Kaluzny Date: Mon, 30 Mar 2026 12:44:11 +0200 Subject: [PATCH 1/7] feat: implement retrieving the active product version by ID --- pkg/api/datastore.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/pkg/api/datastore.go b/pkg/api/datastore.go index 20ebac0..4c498a1 100644 --- a/pkg/api/datastore.go +++ b/pkg/api/datastore.go @@ -330,38 +330,44 @@ query MyQuery { return resp.Data.Products, nil } -type getLatestProductVersionByIDParams struct { +type getActiveProductVersionByIDParams struct { ID string `json:"id"` } -type getLatestProductVersionByIDResponseData struct { - ProductVersions []ProductVersion `json:"ProductVersions"` +type getActiveProductVersionByIDProduct struct { + ActiveVersionID string `json:"active_version_id"` } -type getLatestProductVersionByIDResponse struct { - Data getLatestProductVersionByIDResponseData `json:"data"` +type getActiveProductVersionByIDResponseData struct { + Product *getActiveProductVersionByIDProduct `json:"Products_by_pk"` } -func (ds *Datastore) GetLatestProductVersionByID(ctx context.Context, id string) (ProductVersion, error) { +type getActiveProductVersionByIDResponse struct { + Data getActiveProductVersionByIDResponseData `json:"data"` +} + +// GetActiveProductVersionByID returns the active (live) product version for the given product ID. +// It reads the product's active_version_id directly, avoiding selection of draft or non-live versions. +func (ds *Datastore) GetActiveProductVersionByID(ctx context.Context, id string) (ProductVersion, error) { const query = ` query MyQuery ($id: uuid!) { - ProductVersions(where: {Product: {active_version_id: {_is_null: false},id: {_eq: $id}}}, order_by: {created_at: desc}) { - id + Products_by_pk(id: $id) { + active_version_id } } ` req := GQLRequest{ Query: query, - Variables: getLatestProductVersionByIDParams{ID: id}, + Variables: getActiveProductVersionByIDParams{ID: id}, } - var resp getLatestProductVersionByIDResponse + var resp getActiveProductVersionByIDResponse if err := ds.gqlClient.Do(ctx, req, &resp); err != nil { return ProductVersion{}, err } - if len(resp.Data.ProductVersions) == 0 { + if resp.Data.Product == nil || resp.Data.Product.ActiveVersionID == "" { return ProductVersion{}, ErrNotFound } - return resp.Data.ProductVersions[0], nil + return ProductVersion{ID: resp.Data.Product.ActiveVersionID}, nil } type getLogByIDParams struct { From c207108bccd91c4fb62ebb2497e8836aca32fed0 Mon Sep 17 00:00:00 2001 From: Daniel Kaluzny Date: Mon, 30 Mar 2026 12:44:44 +0200 Subject: [PATCH 2/7] fix: update message and method to retrieve active product template version --- vcr/init/init.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vcr/init/init.go b/vcr/init/init.go index 6d33ace..0fbb9e8 100644 --- a/vcr/init/init.go +++ b/vcr/init/init.go @@ -326,11 +326,11 @@ func askTemplate(ctx context.Context, opts *Options) error { selectedProductID := templateOptions.IDLookup[templateLabel] - spinner = cmdutil.DisplaySpinnerMessageWithHandle(" Retrieve the latest product template version... ") - selectedProductVersion, err := opts.Datastore().GetLatestProductVersionByID(ctx, selectedProductID) + spinner = cmdutil.DisplaySpinnerMessageWithHandle(" Retrieving active product template version... ") + selectedProductVersion, err := opts.Datastore().GetActiveProductVersionByID(ctx, selectedProductID) spinner.Stop() if err != nil { - return fmt.Errorf("failed to get the latest product template version: %w", err) + return fmt.Errorf("failed to get the active product template version: %w", err) } selectedProductVersionID := selectedProductVersion.ID From 07db12e95cfe9c92a10911c181a2906a74766fca Mon Sep 17 00:00:00 2001 From: Daniel Kaluzny Date: Mon, 30 Mar 2026 12:46:35 +0200 Subject: [PATCH 3/7] fix: rename method to retrieve active product version by ID --- pkg/cmdutil/factory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmdutil/factory.go b/pkg/cmdutil/factory.go index 2c10087..03b1785 100644 --- a/pkg/cmdutil/factory.go +++ b/pkg/cmdutil/factory.go @@ -72,7 +72,7 @@ type DatastoreInterface interface { GetRuntimeByName(ctx context.Context, name string) (api.Runtime, error) GetProject(ctx context.Context, accountID, name string) (api.Project, error) ListProducts(ctx context.Context) ([]api.Product, error) - GetLatestProductVersionByID(ctx context.Context, id string) (api.ProductVersion, error) + GetActiveProductVersionByID(ctx context.Context, id string) (api.ProductVersion, error) ListLogsByInstanceID(ctx context.Context, instanceID string, limit int, timestamp time.Time) ([]api.Log, error) } From 8e20a93104562af3bd2ff9bd0d8015833ad7ffb2 Mon Sep 17 00:00:00 2001 From: Daniel Kaluzny Date: Mon, 30 Mar 2026 12:46:52 +0200 Subject: [PATCH 4/7] fix: rename tests and mocks to use active product version by ID --- pkg/api/datastore_test.go | 37 +++++++++---- testutil/mocks/factory.go | 12 ++-- vcr/init/init_test.go | 113 +++++++++++++++++++++++++++++++++----- 3 files changed, 129 insertions(+), 33 deletions(-) diff --git a/pkg/api/datastore_test.go b/pkg/api/datastore_test.go index c0ebba1..d1b6648 100644 --- a/pkg/api/datastore_test.go +++ b/pkg/api/datastore_test.go @@ -751,13 +751,13 @@ func TestListProducts(t *testing.T) { } } -func TestGetLatestProductVersionByID(t *testing.T) { +func TestGetActiveProductVersionByID(t *testing.T) { httpClient := resty.New() httpmock.ActivateNonDefault(httpClient.GetClient()) defer httpmock.DeactivateAndReset() type mock struct { - mockResponse getLatestProductVersionByIDResponse + mockResponse getActiveProductVersionByIDResponse status int } @@ -774,12 +774,10 @@ func TestGetLatestProductVersionByID(t *testing.T) { { name: "200-happy-path", mock: mock{ - mockResponse: getLatestProductVersionByIDResponse{ - Data: getLatestProductVersionByIDResponseData{ - ProductVersions: []ProductVersion{ - { - ID: "ProductVersion1-id", - }, + mockResponse: getActiveProductVersionByIDResponse{ + Data: getActiveProductVersionByIDResponseData{ + Product: &getActiveProductVersionByIDProduct{ + ActiveVersionID: "ProductVersion1-id", }, }, }, @@ -796,9 +794,24 @@ func TestGetLatestProductVersionByID(t *testing.T) { { name: "404-error", mock: mock{ - mockResponse: getLatestProductVersionByIDResponse{ - Data: getLatestProductVersionByIDResponseData{ - ProductVersions: []ProductVersion{}, + mockResponse: getActiveProductVersionByIDResponse{ + Data: getActiveProductVersionByIDResponseData{}, + }, + status: http.StatusOK, + }, + want: want{ + output: ProductVersion{}, + err: ErrNotFound, + }, + }, + { + name: "404-error-empty-active-version-id", + mock: mock{ + mockResponse: getActiveProductVersionByIDResponse{ + Data: getActiveProductVersionByIDResponseData{ + Product: &getActiveProductVersionByIDProduct{ + ActiveVersionID: "", + }, }, }, status: http.StatusOK, @@ -829,7 +842,7 @@ func TestGetLatestProductVersionByID(t *testing.T) { gqlClient := NewGraphQLClient("https://example.com", httpClient) datastoreClient := NewDatastore(gqlClient) - output, err := datastoreClient.GetLatestProductVersionByID(t.Context(), "Product1-id") + output, err := datastoreClient.GetActiveProductVersionByID(t.Context(), "Product1-id") if tt.want.err != nil { require.EqualError(t, err, tt.want.err.Error()) httpmock.Reset() diff --git a/testutil/mocks/factory.go b/testutil/mocks/factory.go index 66f6d3d..281f7a6 100644 --- a/testutil/mocks/factory.go +++ b/testutil/mocks/factory.go @@ -639,19 +639,19 @@ func (mr *MockDatastoreInterfaceMockRecorder) GetInstanceByProjectAndInstanceNam return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceByProjectAndInstanceName", reflect.TypeOf((*MockDatastoreInterface)(nil).GetInstanceByProjectAndInstanceName), ctx, projectName, instanceName) } -// GetLatestProductVersionByID mocks base method. -func (m *MockDatastoreInterface) GetLatestProductVersionByID(ctx context.Context, id string) (api.ProductVersion, error) { +// GetActiveProductVersionByID mocks base method. +func (m *MockDatastoreInterface) GetActiveProductVersionByID(ctx context.Context, id string) (api.ProductVersion, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLatestProductVersionByID", ctx, id) + ret := m.ctrl.Call(m, "GetActiveProductVersionByID", ctx, id) ret0, _ := ret[0].(api.ProductVersion) ret1, _ := ret[1].(error) return ret0, ret1 } -// GetLatestProductVersionByID indicates an expected call of GetLatestProductVersionByID. -func (mr *MockDatastoreInterfaceMockRecorder) GetLatestProductVersionByID(ctx, id interface{}) *gomock.Call { +// GetActiveProductVersionByID indicates an expected call of GetActiveProductVersionByID. +func (mr *MockDatastoreInterfaceMockRecorder) GetActiveProductVersionByID(ctx, id interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestProductVersionByID", reflect.TypeOf((*MockDatastoreInterface)(nil).GetLatestProductVersionByID), ctx, id) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveProductVersionByID", reflect.TypeOf((*MockDatastoreInterface)(nil).GetActiveProductVersionByID), ctx, id) } // GetProject mocks base method. diff --git a/vcr/init/init_test.go b/vcr/init/init_test.go index 4c8b0fd..4f076a3 100644 --- a/vcr/init/init_test.go +++ b/vcr/init/init_test.go @@ -105,9 +105,9 @@ func TestInit(t *testing.T) { InitTemplateAskForUserChoiceTimes int InitReturnTemplateLabel string InitTemplateAskForUserChoiceErr error - InitGetLatestProductVersionByIDTimes int - InitGetLatestProductVersionByIDReturnTemplate api.ProductVersion - InitGetLatestProductVersionByIDReturnErr error + InitGetActiveProductVersionByIDTimes int + InitGetActiveProductVersionByIDReturnTemplate api.ProductVersion + InitGetActiveProductVersionByIDReturnErr error InitGetTemplateTimes int InitGetTemplateReturnTemplate []byte InitGetTemplateReturnErr error @@ -196,9 +196,9 @@ func TestInit(t *testing.T) { InitReturnTemplateLabel: "template-label", InitTemplateAskForUserChoiceErr: nil, - InitGetLatestProductVersionByIDTimes: 0, - InitGetLatestProductVersionByIDReturnTemplate: api.ProductVersion{}, - InitGetLatestProductVersionByIDReturnErr: nil, + InitGetActiveProductVersionByIDTimes: 0, + InitGetActiveProductVersionByIDReturnTemplate: api.ProductVersion{}, + InitGetActiveProductVersionByIDReturnErr: nil, InitGetTemplateTimes: 0, InitGetTemplateReturnTemplate: []byte{}, InitGetTemplateReturnErr: nil, @@ -280,9 +280,9 @@ func TestInit(t *testing.T) { InitReturnTemplateLabel: "template-label", InitTemplateAskForUserChoiceErr: nil, - InitGetLatestProductVersionByIDTimes: 0, - InitGetLatestProductVersionByIDReturnTemplate: api.ProductVersion{}, - InitGetLatestProductVersionByIDReturnErr: nil, + InitGetActiveProductVersionByIDTimes: 0, + InitGetActiveProductVersionByIDReturnTemplate: api.ProductVersion{}, + InitGetActiveProductVersionByIDReturnErr: nil, InitGetTemplateTimes: 0, InitGetTemplateReturnTemplate: []byte{}, InitGetTemplateReturnErr: nil, @@ -364,9 +364,9 @@ func TestInit(t *testing.T) { InitReturnTemplateLabel: "product-name", InitTemplateAskForUserChoiceErr: nil, - InitGetLatestProductVersionByIDTimes: 1, - InitGetLatestProductVersionByIDReturnTemplate: api.ProductVersion{ID: "product-version-id"}, - InitGetLatestProductVersionByIDReturnErr: nil, + InitGetActiveProductVersionByIDTimes: 1, + InitGetActiveProductVersionByIDReturnTemplate: api.ProductVersion{ID: "product-version-id"}, + InitGetActiveProductVersionByIDReturnErr: nil, InitGetTemplateTimes: 1, InitGetTemplateReturnTemplate: byteSlice, InitGetTemplateReturnErr: nil, @@ -375,6 +375,89 @@ func TestInit(t *testing.T) { stdout: fmt.Sprintf("✓ %s/vcr.yml created\n", absPath), }, }, + { + name: "error-when-active-product-version-not-found", + cli: "testdata/", + mock: mock{ + InitProjNameAskForUserInputQuestion: "Enter your project name:", + InitProjNameAskForUserInputTimes: 1, + InitReturnProjName: "project-name", + InitProjNameAskForUserInputErr: nil, + + InitInstListVonageAppsFilter: "", + InitInstListVonageAppsTimes: 1, + InitReturnInstApps: api.ListVonageApplicationsOutput{Applications: []api.ApplicationListItem{{Name: "app-name", ID: "app-id"}}}, + InitInstListVonageAppsReturnErr: nil, + InitInstAskForUserChoiceQuestion: "Select your Vonage application ID for deployment:", + InitInstAskForUserChoiceTimes: 1, + InitReturnInstAppLabel: "app-name - (app-id)", + InitInstAskForUserChoiceErr: nil, + InitInstAppNameAskForUserInputQuestion: "Enter your new Vonage application name for deployment:", + InitInstAppNameAskForUserInputTimes: 0, + InitReturnInstAppName: "app-name", + InitInstAppAskForUserInputErr: nil, + InitInstCreateTimes: 0, + InitInstCreateReturnApp: api.CreateVonageApplicationOutput{}, + InitInstCreateReturnErr: nil, + InitInstCreateName: "app-name", + + InitDebugListVonageAppsFilter: "", + InitDebugListVonageAppsTimes: 1, + InitReturnDebugApps: api.ListVonageApplicationsOutput{Applications: []api.ApplicationListItem{{Name: "app-name", ID: "app-id"}}}, + InitDebugListVonageAppsReturnErr: nil, + InitDebugAskForUserChoiceQuestion: "Select your Vonage application ID for debug:", + InitDebugAskForUserChoiceTimes: 1, + InitReturnDebugAppLabel: "app-name - (app-id)", + InitDebugAskForUserChoiceErr: nil, + InitDebugAppNameAskForUserInputQuestion: "Enter your new Vonage application name for debug:", + InitDebugAppNameAskForUserInputTimes: 0, + InitReturnDebugAppName: "app-name", + InitDebugAppAskForUserInputErr: nil, + InitDebugCreateTimes: 0, + InitDebugCreateReturnApp: api.CreateVonageApplicationOutput{}, + InitDebugCreateReturnErr: nil, + InitDebugCreateName: "app-name", + + InitListRuntimesTimes: 1, + InitReturnRuntimes: []api.Runtime{{Name: "nodejs16", Comments: "", Language: "nodejs"}}, + InitListRuntimesReturnErr: nil, + InitRuntimeAskForUserChoiceQuestion: "Select a runtime:", + InitRuntimeAskForUserChoiceTimes: 1, + InitReturnRuntimeLabel: "nodejs16", + InitRuntimeAskForUserChoiceErr: nil, + + InitListRegionsTimes: 1, + InitReturnRegions: []api.Region{{Name: "AWS - Europe Ireland", Alias: "aws.euw1"}}, + InitListRegionsReturnErr: nil, + InitRegionAskForUserChoiceQuestion: "Select a region:", + InitRegionAskForUserChoiceTimes: 1, + InitReturnRegionLabel: "AWS - Europe Ireland - (aws.euw1)", + InitRegionAskForUserChoiceErr: nil, + + InitInstNameAskForUserInputQuestion: "Enter your Instance name:", + InitInstNameAskForUserInputTimes: 1, + InitReturnInstName: "instance-name", + InitInstNameAskForUserInputErr: nil, + + InitListProductsTimes: 1, + InitReturnProducts: []api.Product{{ID: "product-id", Name: "product-name", ProgrammingLanguage: "NodeJS"}}, + InitListProductsReturnErr: nil, + InitTemplateAskForUserChoiceQuestion: "Select a product template for runtime nodejs16: ", + InitTemplateAskForUserChoiceTimes: 1, + InitReturnTemplateLabel: "product-name", + InitTemplateAskForUserChoiceErr: nil, + + InitGetActiveProductVersionByIDTimes: 1, + InitGetActiveProductVersionByIDReturnTemplate: api.ProductVersion{}, + InitGetActiveProductVersionByIDReturnErr: api.ErrNotFound, + InitGetTemplateTimes: 0, + InitGetTemplateReturnTemplate: []byte{}, + InitGetTemplateReturnErr: nil, + }, + want: want{ + errMsg: "failed to get the active product template version: not found", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -449,9 +532,9 @@ func TestInit(t *testing.T) { Times(tt.mock.InitTemplateAskForUserChoiceTimes). Return(tt.mock.InitReturnTemplateLabel, tt.mock.InitTemplateAskForUserChoiceErr) - datastoreMock.EXPECT().GetLatestProductVersionByID(gomock.Any(), gomock.Any()). - Times(tt.mock.InitGetLatestProductVersionByIDTimes). - Return(tt.mock.InitGetLatestProductVersionByIDReturnTemplate, tt.mock.InitGetLatestProductVersionByIDReturnErr) + datastoreMock.EXPECT().GetActiveProductVersionByID(gomock.Any(), gomock.Any()). + Times(tt.mock.InitGetActiveProductVersionByIDTimes). + Return(tt.mock.InitGetActiveProductVersionByIDReturnTemplate, tt.mock.InitGetActiveProductVersionByIDReturnErr) marketplaceMock.EXPECT().GetTemplate(gomock.Any(), gomock.Any(), gomock.Any()). Times(tt.mock.InitGetTemplateTimes). From e907812a1758c0139a37b5efd2c7d3f953a2e43a Mon Sep 17 00:00:00 2001 From: Daniel Kaluzny Date: Tue, 5 May 2026 08:31:45 +0200 Subject: [PATCH 5/7] fix: resolve init template version from product active_version_id --- pkg/api/datastore.go | 35 ++++++++++++++++-- pkg/api/datastore_test.go | 76 ++++++++++++++++++++++++++++----------- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/pkg/api/datastore.go b/pkg/api/datastore.go index 4c498a1..c05792f 100644 --- a/pkg/api/datastore.go +++ b/pkg/api/datastore.go @@ -346,10 +346,18 @@ type getActiveProductVersionByIDResponse struct { Data getActiveProductVersionByIDResponseData `json:"data"` } +type getProductVersionByIDResponseData struct { + ProductVersion *ProductVersion `json:"ProductVersions_by_pk"` +} + +type getProductVersionByIDResponse struct { + Data getProductVersionByIDResponseData `json:"data"` +} + // GetActiveProductVersionByID returns the active (live) product version for the given product ID. // It reads the product's active_version_id directly, avoiding selection of draft or non-live versions. func (ds *Datastore) GetActiveProductVersionByID(ctx context.Context, id string) (ProductVersion, error) { - const query = ` + const getProductQuery = ` query MyQuery ($id: uuid!) { Products_by_pk(id: $id) { active_version_id @@ -357,7 +365,7 @@ query MyQuery ($id: uuid!) { } ` req := GQLRequest{ - Query: query, + Query: getProductQuery, Variables: getActiveProductVersionByIDParams{ID: id}, } var resp getActiveProductVersionByIDResponse @@ -367,7 +375,28 @@ query MyQuery ($id: uuid!) { if resp.Data.Product == nil || resp.Data.Product.ActiveVersionID == "" { return ProductVersion{}, ErrNotFound } - return ProductVersion{ID: resp.Data.Product.ActiveVersionID}, nil + + const getVersionQuery = ` +query MyQuery ($id: uuid!) { + ProductVersions_by_pk(id: $id) { + id + } +} +` + activeVersionID := resp.Data.Product.ActiveVersionID + versionReq := GQLRequest{ + Query: getVersionQuery, + Variables: getActiveProductVersionByIDParams{ID: activeVersionID}, + } + var versionResp getProductVersionByIDResponse + if err := ds.gqlClient.Do(ctx, versionReq, &versionResp); err != nil { + return ProductVersion{}, err + } + if versionResp.Data.ProductVersion == nil || versionResp.Data.ProductVersion.ID == "" { + return ProductVersion{}, ErrNotFound + } + + return *versionResp.Data.ProductVersion, nil } type getLogByIDParams struct { diff --git a/pkg/api/datastore_test.go b/pkg/api/datastore_test.go index d1b6648..a67e5dc 100644 --- a/pkg/api/datastore_test.go +++ b/pkg/api/datastore_test.go @@ -757,8 +757,8 @@ func TestGetActiveProductVersionByID(t *testing.T) { defer httpmock.DeactivateAndReset() type mock struct { - mockResponse getActiveProductVersionByIDResponse - status int + mockResponses []interface{} + status int } type want struct { @@ -774,10 +774,17 @@ func TestGetActiveProductVersionByID(t *testing.T) { { name: "200-happy-path", mock: mock{ - mockResponse: getActiveProductVersionByIDResponse{ - Data: getActiveProductVersionByIDResponseData{ - Product: &getActiveProductVersionByIDProduct{ - ActiveVersionID: "ProductVersion1-id", + mockResponses: []interface{}{ + getActiveProductVersionByIDResponse{ + Data: getActiveProductVersionByIDResponseData{ + Product: &getActiveProductVersionByIDProduct{ + ActiveVersionID: "ProductVersion1-id", + }, + }, + }, + getProductVersionByIDResponse{ + Data: getProductVersionByIDResponseData{ + ProductVersion: &ProductVersion{ID: "ProductVersion1-id"}, }, }, }, @@ -794,8 +801,10 @@ func TestGetActiveProductVersionByID(t *testing.T) { { name: "404-error", mock: mock{ - mockResponse: getActiveProductVersionByIDResponse{ - Data: getActiveProductVersionByIDResponseData{}, + mockResponses: []interface{}{ + getActiveProductVersionByIDResponse{ + Data: getActiveProductVersionByIDResponseData{}, + }, }, status: http.StatusOK, }, @@ -807,12 +816,36 @@ func TestGetActiveProductVersionByID(t *testing.T) { { name: "404-error-empty-active-version-id", mock: mock{ - mockResponse: getActiveProductVersionByIDResponse{ - Data: getActiveProductVersionByIDResponseData{ - Product: &getActiveProductVersionByIDProduct{ - ActiveVersionID: "", + mockResponses: []interface{}{ + getActiveProductVersionByIDResponse{ + Data: getActiveProductVersionByIDResponseData{ + Product: &getActiveProductVersionByIDProduct{ + ActiveVersionID: "", + }, + }, + }, + }, + status: http.StatusOK, + }, + want: want{ + output: ProductVersion{}, + err: ErrNotFound, + }, + }, + { + name: "404-error-active-version-not-found", + mock: mock{ + mockResponses: []interface{}{ + getActiveProductVersionByIDResponse{ + Data: getActiveProductVersionByIDResponseData{ + Product: &getActiveProductVersionByIDProduct{ + ActiveVersionID: "ProductVersion1-id", + }, }, }, + getProductVersionByIDResponse{ + Data: getProductVersionByIDResponseData{}, + }, }, status: http.StatusOK, }, @@ -824,16 +857,19 @@ func TestGetActiveProductVersionByID(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - - jsonData, err := json.Marshal(tt.mock.mockResponse) - if err != nil { - t.Fatalf("Error occurred during marshaling. Error: %s", err.Error()) - } - - mockResponse := string(jsonData) - + responseCallCount := 0 httpmock.RegisterResponder("POST", "https://example.com", func(_ *http.Request) (*http.Response, error) { + mockIndex := responseCallCount + if mockIndex >= len(tt.mock.mockResponses) { + mockIndex = len(tt.mock.mockResponses) - 1 + } + jsonData, err := json.Marshal(tt.mock.mockResponses[mockIndex]) + if err != nil { + t.Fatalf("Error occurred during marshaling. Error: %s", err.Error()) + } + responseCallCount++ + mockResponse := string(jsonData) resp := httpmock.NewStringResponse(tt.mock.status, mockResponse) resp.Header.Set("Content-Type", "application/json") return resp, nil From 50acb093a737e8e91815b6e9f4359b91374b05d9 Mon Sep 17 00:00:00 2001 From: Daniel Kaluzny Date: Thu, 14 May 2026 13:21:46 +0200 Subject: [PATCH 6/7] fix: add regression test for retrieving active product version over newer draft --- pkg/api/datastore_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pkg/api/datastore_test.go b/pkg/api/datastore_test.go index a67e5dc..d4605e5 100644 --- a/pkg/api/datastore_test.go +++ b/pkg/api/datastore_test.go @@ -854,6 +854,35 @@ func TestGetActiveProductVersionByID(t *testing.T) { err: ErrNotFound, }, }, + // Regression: must return the product's active version, not the latest by creation order. + { + name: "200-active-version-returned-when-newer-draft-exists", + mock: mock{ + mockResponses: []interface{}{ + // First call: product lookup — active_version_id points to v1 (the live version), + // NOT v2 which was created more recently but is a draft without source code. + getActiveProductVersionByIDResponse{ + Data: getActiveProductVersionByIDResponseData{ + Product: &getActiveProductVersionByIDProduct{ + ActiveVersionID: "v1-active-id", + }, + }, + }, + // Second call: version lookup resolves v1 by its primary key. + // v2 ("v2-draft-id") is never queried — it is skipped entirely. + getProductVersionByIDResponse{ + Data: getProductVersionByIDResponseData{ + ProductVersion: &ProductVersion{ID: "v1-active-id"}, + }, + }, + }, + status: http.StatusOK, + }, + want: want{ + output: ProductVersion{ID: "v1-active-id"}, + err: nil, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From d7d9fb3476bb43ae38de6d5f924043a5586da0e2 Mon Sep 17 00:00:00 2001 From: Daniel Kaluzny Date: Tue, 19 May 2026 09:32:15 +0200 Subject: [PATCH 7/7] fix: removing unnecessary version lookup --- pkg/api/datastore.go | 30 +----------------------------- pkg/api/datastore_test.go | 36 +----------------------------------- 2 files changed, 2 insertions(+), 64 deletions(-) diff --git a/pkg/api/datastore.go b/pkg/api/datastore.go index c05792f..767b01d 100644 --- a/pkg/api/datastore.go +++ b/pkg/api/datastore.go @@ -346,14 +346,6 @@ type getActiveProductVersionByIDResponse struct { Data getActiveProductVersionByIDResponseData `json:"data"` } -type getProductVersionByIDResponseData struct { - ProductVersion *ProductVersion `json:"ProductVersions_by_pk"` -} - -type getProductVersionByIDResponse struct { - Data getProductVersionByIDResponseData `json:"data"` -} - // GetActiveProductVersionByID returns the active (live) product version for the given product ID. // It reads the product's active_version_id directly, avoiding selection of draft or non-live versions. func (ds *Datastore) GetActiveProductVersionByID(ctx context.Context, id string) (ProductVersion, error) { @@ -376,27 +368,7 @@ query MyQuery ($id: uuid!) { return ProductVersion{}, ErrNotFound } - const getVersionQuery = ` -query MyQuery ($id: uuid!) { - ProductVersions_by_pk(id: $id) { - id - } -} -` - activeVersionID := resp.Data.Product.ActiveVersionID - versionReq := GQLRequest{ - Query: getVersionQuery, - Variables: getActiveProductVersionByIDParams{ID: activeVersionID}, - } - var versionResp getProductVersionByIDResponse - if err := ds.gqlClient.Do(ctx, versionReq, &versionResp); err != nil { - return ProductVersion{}, err - } - if versionResp.Data.ProductVersion == nil || versionResp.Data.ProductVersion.ID == "" { - return ProductVersion{}, ErrNotFound - } - - return *versionResp.Data.ProductVersion, nil + return ProductVersion{ID: resp.Data.Product.ActiveVersionID}, nil } type getLogByIDParams struct { diff --git a/pkg/api/datastore_test.go b/pkg/api/datastore_test.go index d4605e5..bc0f27c 100644 --- a/pkg/api/datastore_test.go +++ b/pkg/api/datastore_test.go @@ -782,11 +782,6 @@ func TestGetActiveProductVersionByID(t *testing.T) { }, }, }, - getProductVersionByIDResponse{ - Data: getProductVersionByIDResponseData{ - ProductVersion: &ProductVersion{ID: "ProductVersion1-id"}, - }, - }, }, status: http.StatusOK, }, @@ -832,34 +827,12 @@ func TestGetActiveProductVersionByID(t *testing.T) { err: ErrNotFound, }, }, - { - name: "404-error-active-version-not-found", - mock: mock{ - mockResponses: []interface{}{ - getActiveProductVersionByIDResponse{ - Data: getActiveProductVersionByIDResponseData{ - Product: &getActiveProductVersionByIDProduct{ - ActiveVersionID: "ProductVersion1-id", - }, - }, - }, - getProductVersionByIDResponse{ - Data: getProductVersionByIDResponseData{}, - }, - }, - status: http.StatusOK, - }, - want: want{ - output: ProductVersion{}, - err: ErrNotFound, - }, - }, // Regression: must return the product's active version, not the latest by creation order. { name: "200-active-version-returned-when-newer-draft-exists", mock: mock{ mockResponses: []interface{}{ - // First call: product lookup — active_version_id points to v1 (the live version), + // Product lookup — active_version_id points to v1 (the live version), // NOT v2 which was created more recently but is a draft without source code. getActiveProductVersionByIDResponse{ Data: getActiveProductVersionByIDResponseData{ @@ -868,13 +841,6 @@ func TestGetActiveProductVersionByID(t *testing.T) { }, }, }, - // Second call: version lookup resolves v1 by its primary key. - // v2 ("v2-draft-id") is never queried — it is skipped entirely. - getProductVersionByIDResponse{ - Data: getProductVersionByIDResponseData{ - ProductVersion: &ProductVersion{ID: "v1-active-id"}, - }, - }, }, status: http.StatusOK, },