From decd422e201c0b2e39c46fb7567bd9cf70ef7753 Mon Sep 17 00:00:00 2001 From: manjari Date: Wed, 25 Feb 2026 19:53:24 +0000 Subject: [PATCH 1/4] test(storage): Replace handwritten storage mocks with gomock bazel rule --- extension/storage/BUILD.bazel | 13 + extension/storage/batch_dependent_store.go | 2 + extension/storage/batch_store.go | 2 + extension/storage/build_store.go | 2 + extension/storage/change_provider_store.go | 2 + extension/storage/mock/BUILD.bazel | 87 ++++++ extension/storage/request_store.go | 2 + extension/storage/speculation_tree_store.go | 2 + extension/storage/storage.go | 2 + gateway/controller/BUILD.bazel | 3 +- gateway/controller/land_test.go | 324 ++++---------------- 11 files changed, 173 insertions(+), 268 deletions(-) create mode 100644 extension/storage/mock/BUILD.bazel diff --git a/extension/storage/BUILD.bazel b/extension/storage/BUILD.bazel index fa34c18d..5278e5b8 100644 --- a/extension/storage/BUILD.bazel +++ b/extension/storage/BUILD.bazel @@ -1,5 +1,18 @@ load("@rules_go//go:def.bzl", "go_library") +exports_files( + [ + "batch_dependent_store.go", + "batch_store.go", + "build_store.go", + "change_provider_store.go", + "request_store.go", + "speculation_tree_store.go", + "storage.go", + ], + visibility = ["//extension/storage/mock:__pkg__"], +) + go_library( name = "storage", srcs = [ diff --git a/extension/storage/batch_dependent_store.go b/extension/storage/batch_dependent_store.go index 4f120039..3329dbcc 100644 --- a/extension/storage/batch_dependent_store.go +++ b/extension/storage/batch_dependent_store.go @@ -1,5 +1,7 @@ package storage +//go:generate mockgen -source=batch_dependent_store.go -destination=mock/batch_dependent_store.go -package=mock + import ( "context" diff --git a/extension/storage/batch_store.go b/extension/storage/batch_store.go index 6bc53976..64e8be83 100644 --- a/extension/storage/batch_store.go +++ b/extension/storage/batch_store.go @@ -1,5 +1,7 @@ package storage +//go:generate mockgen -source=batch_store.go -destination=mock/batch_store.go -package=mock + import ( "context" diff --git a/extension/storage/build_store.go b/extension/storage/build_store.go index 0f31795a..386ba925 100644 --- a/extension/storage/build_store.go +++ b/extension/storage/build_store.go @@ -1,5 +1,7 @@ package storage +//go:generate mockgen -source=build_store.go -destination=mock/build_store.go -package=mock + import ( "context" diff --git a/extension/storage/change_provider_store.go b/extension/storage/change_provider_store.go index ae913378..bf3e9c4b 100644 --- a/extension/storage/change_provider_store.go +++ b/extension/storage/change_provider_store.go @@ -1,5 +1,7 @@ package storage +//go:generate mockgen -source=change_provider_store.go -destination=mock/change_provider_store.go -package=mock + import ( "context" diff --git a/extension/storage/mock/BUILD.bazel b/extension/storage/mock/BUILD.bazel new file mode 100644 index 00000000..1ff6df23 --- /dev/null +++ b/extension/storage/mock/BUILD.bazel @@ -0,0 +1,87 @@ +load("@rules_go//extras:gomock.bzl", "gomock") +load("@rules_go//go:def.bzl", "go_library") + +_MOCKGEN = "@org_uber_go_mock//mockgen" + +gomock( + name = "mock_storage_src", + out = "storage_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:storage.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", +) + +gomock( + name = "mock_request_store_src", + out = "request_store_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:request_store.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", +) + +gomock( + name = "mock_change_provider_store_src", + out = "change_provider_store_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:change_provider_store.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", +) + +gomock( + name = "mock_batch_store_src", + out = "batch_store_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:batch_store.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", +) + +gomock( + name = "mock_batch_dependent_store_src", + out = "batch_dependent_store_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:batch_dependent_store.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", +) + +gomock( + name = "mock_build_store_src", + out = "build_store_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:build_store.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", +) + +gomock( + name = "mock_speculation_tree_store_src", + out = "speculation_tree_store_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:speculation_tree_store.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", +) + +go_library( + name = "mock", + srcs = [ + ":mock_batch_dependent_store_src", + ":mock_batch_store_src", + ":mock_build_store_src", + ":mock_change_provider_store_src", + ":mock_request_store_src", + ":mock_speculation_tree_store_src", + ":mock_storage_src", + ], + importpath = "github.com/uber/submitqueue/extension/storage/mock", + visibility = ["//visibility:public"], + deps = [ + "//entity", + "//extension/storage", + "@org_uber_go_mock//gomock", + ], +) diff --git a/extension/storage/request_store.go b/extension/storage/request_store.go index bb84fb3e..cff17c33 100644 --- a/extension/storage/request_store.go +++ b/extension/storage/request_store.go @@ -1,5 +1,7 @@ package storage +//go:generate mockgen -source=request_store.go -destination=mock/request_store.go -package=mock + import ( "context" diff --git a/extension/storage/speculation_tree_store.go b/extension/storage/speculation_tree_store.go index cf285f05..c62b2ead 100644 --- a/extension/storage/speculation_tree_store.go +++ b/extension/storage/speculation_tree_store.go @@ -1,5 +1,7 @@ package storage +//go:generate mockgen -source=speculation_tree_store.go -destination=mock/speculation_tree_store.go -package=mock + import ( "context" diff --git a/extension/storage/storage.go b/extension/storage/storage.go index e87d69c5..8aeef18f 100644 --- a/extension/storage/storage.go +++ b/extension/storage/storage.go @@ -1,5 +1,7 @@ package storage +//go:generate mockgen -source=storage.go -destination=mock/storage.go -package=mock + import "errors" import "fmt" diff --git a/gateway/controller/BUILD.bazel b/gateway/controller/BUILD.bazel index 808b81b3..c7011821 100644 --- a/gateway/controller/BUILD.bazel +++ b/gateway/controller/BUILD.bazel @@ -30,11 +30,12 @@ go_test( deps = [ "//entity", "//entity/queue", - "//extension/storage", + "//extension/storage/mock", "//gateway/protopb", "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", "@com_github_uber_go_tally_v4//:tally", + "@org_uber_go_mock//gomock", "@org_uber_go_zap//:zap", ], ) diff --git a/gateway/controller/land_test.go b/gateway/controller/land_test.go index bcd2bfbe..cd2ba2b5 100644 --- a/gateway/controller/land_test.go +++ b/gateway/controller/land_test.go @@ -10,8 +10,9 @@ import ( "github.com/uber-go/tally/v4" "github.com/uber/submitqueue/entity" "github.com/uber/submitqueue/entity/queue" - "github.com/uber/submitqueue/extension/storage" + storagemock "github.com/uber/submitqueue/extension/storage/mock" pb "github.com/uber/submitqueue/gateway/protopb" + "go.uber.org/mock/gomock" "go.uber.org/zap" ) @@ -23,175 +24,6 @@ func (m *mockCounter) Next(ctx context.Context, domain string) (int64, error) { return m.nextFunc(ctx, domain) } -type mockRequestStore struct { - createFunc func(ctx context.Context, request entity.Request) error -} - -func (m *mockRequestStore) Get(ctx context.Context, id string) (entity.Request, error) { - return entity.Request{}, nil -} - -func (m *mockRequestStore) Create(ctx context.Context, request entity.Request) error { - return m.createFunc(ctx, request) -} - -func (m *mockRequestStore) UpdateState(ctx context.Context, id string, version int32, newState entity.RequestState) error { - return nil -} - -type mockChangeProviderStore struct { - createFunc func(ctx context.Context, changeProvider entity.ChangeProvider) error -} - -func (m *mockChangeProviderStore) Get(ctx context.Context, requestID string) ([]entity.ChangeProvider, error) { - return nil, nil -} - -func (m *mockChangeProviderStore) Create(ctx context.Context, changeProvider entity.ChangeProvider) error { - return m.createFunc(ctx, changeProvider) -} - -type mockBatchStore struct { - createFunc func(ctx context.Context, batch entity.Batch) error - getFunc func(ctx context.Context, id string) (entity.Batch, error) - updateStateFunc func(ctx context.Context, id string, version int32, newState entity.BatchState) error -} - -func (m *mockBatchStore) Get(ctx context.Context, id string) (entity.Batch, error) { - if m.getFunc != nil { - return m.getFunc(ctx, id) - } - return entity.Batch{}, nil -} - -func (m *mockBatchStore) Create(ctx context.Context, batch entity.Batch) error { - if m.createFunc != nil { - return m.createFunc(ctx, batch) - } - return nil -} - -func (m *mockBatchStore) UpdateState(ctx context.Context, id string, version int32, newState entity.BatchState) error { - if m.updateStateFunc != nil { - return m.updateStateFunc(ctx, id, version, newState) - } - return nil -} - -func (m *mockBatchStore) GetByQueueAndStates(ctx context.Context, queue string, states []entity.BatchState) ([]entity.Batch, error) { - return nil, nil -} - -type mockBatchDependentStore struct { - createFunc func(ctx context.Context, batchDependent entity.BatchDependent) error - getFunc func(ctx context.Context, batchID string) (entity.BatchDependent, error) -} - -func (m *mockBatchDependentStore) Get(ctx context.Context, batchID string) (entity.BatchDependent, error) { - if m.getFunc != nil { - return m.getFunc(ctx, batchID) - } - return entity.BatchDependent{}, nil -} - -func (m *mockBatchDependentStore) Create(ctx context.Context, batchDependent entity.BatchDependent) error { - if m.createFunc != nil { - return m.createFunc(ctx, batchDependent) - } - return nil -} - -type mockBuildStore struct { - createFunc func(ctx context.Context, build entity.Build) error - getFunc func(ctx context.Context, id string) (entity.Build, error) - updateStatusFunc func(ctx context.Context, id string, newStatus entity.BuildStatus) error -} - -func (m *mockBuildStore) Get(ctx context.Context, id string) (entity.Build, error) { - if m.getFunc != nil { - return m.getFunc(ctx, id) - } - return entity.Build{}, nil -} - -func (m *mockBuildStore) Create(ctx context.Context, build entity.Build) error { - if m.createFunc != nil { - return m.createFunc(ctx, build) - } - return nil -} - -func (m *mockBuildStore) UpdateStatus(ctx context.Context, id string, newStatus entity.BuildStatus) error { - if m.updateStatusFunc != nil { - return m.updateStatusFunc(ctx, id, newStatus) - } - return nil -} - -type mockSpeculationTreeStore struct { - createFunc func(ctx context.Context, speculationTree entity.SpeculationTree) error - getFunc func(ctx context.Context, batchID string) (entity.SpeculationTree, error) - updateSpeculationsFunc func(ctx context.Context, batchID string, speculations []map[string]string) error -} - -func (m *mockSpeculationTreeStore) Get(ctx context.Context, batchID string) (entity.SpeculationTree, error) { - if m.getFunc != nil { - return m.getFunc(ctx, batchID) - } - return entity.SpeculationTree{}, nil -} - -func (m *mockSpeculationTreeStore) Create(ctx context.Context, speculationTree entity.SpeculationTree) error { - if m.createFunc != nil { - return m.createFunc(ctx, speculationTree) - } - return nil -} - -func (m *mockSpeculationTreeStore) UpdateSpeculations(ctx context.Context, batchID string, speculations []map[string]string) error { - if m.updateSpeculationsFunc != nil { - return m.updateSpeculationsFunc(ctx, batchID, speculations) - } - return nil -} - -type mockStorage struct { - requestStore storage.RequestStore - changeProviderStore storage.ChangeProviderStore - batchStore storage.BatchStore - batchDependentStore storage.BatchDependentStore - buildStore storage.BuildStore - speculationTreeStore storage.SpeculationTreeStore -} - -func (m *mockStorage) GetRequestStore() storage.RequestStore { - return m.requestStore -} - -func (m *mockStorage) GetChangeProviderStore() storage.ChangeProviderStore { - return m.changeProviderStore -} - -func (m *mockStorage) GetBatchStore() storage.BatchStore { - return m.batchStore -} - -func (m *mockStorage) GetBatchDependentStore() storage.BatchDependentStore { - return m.batchDependentStore -} - -func (m *mockStorage) GetBuildStore() storage.BuildStore { - return m.buildStore -} - -func (m *mockStorage) GetSpeculationTreeStore() storage.SpeculationTreeStore { - return m.speculationTreeStore -} - -func (m *mockStorage) Close() error { - return nil -} - type mockPublisher struct { publishFunc func(ctx context.Context, topic string, msg queue.Message) error } @@ -212,18 +44,9 @@ func noopPublisher() *mockPublisher { } func TestNewLandController(t *testing.T) { - store := &mockStorage{ - requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }, - changeProviderStore: &mockChangeProviderStore{ - createFunc: func(ctx context.Context, changeProvider entity.ChangeProvider) error { - return nil - }, - }, - } + ctrl := gomock.NewController(t) + store := storagemock.NewMockStorage(ctrl) + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 1, nil }} @@ -232,18 +55,12 @@ func TestNewLandController(t *testing.T) { } func TestLand_ReturnsSqid(t *testing.T) { - store := &mockStorage{ - requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }, - changeProviderStore: &mockChangeProviderStore{ - createFunc: func(ctx context.Context, changeProvider entity.ChangeProvider) error { - return nil - }, - }, - } + ctrl := gomock.NewController(t) + mockReqStore := storagemock.NewMockRequestStore(ctrl) + mockReqStore.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) + store := storagemock.NewMockStorage(ctrl) + store.EXPECT().GetRequestStore().Return(mockReqStore).AnyTimes() + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 1, nil }} @@ -263,19 +80,17 @@ func TestLand_ReturnsSqid(t *testing.T) { func TestLand_PassesCorrectParametersToStore(t *testing.T) { var capturedRequest entity.Request - store := &mockStorage{ - requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - capturedRequest = request - return nil - }, - }, - changeProviderStore: &mockChangeProviderStore{ - createFunc: func(ctx context.Context, changeProvider entity.ChangeProvider) error { - return nil - }, + ctrl := gomock.NewController(t) + mockReqStore := storagemock.NewMockRequestStore(ctrl) + mockReqStore.EXPECT().Create(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, request entity.Request) error { + capturedRequest = request + return nil }, - } + ) + store := storagemock.NewMockStorage(ctrl) + store.EXPECT().GetRequestStore().Return(mockReqStore).AnyTimes() + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 42, nil }} @@ -300,18 +115,12 @@ func TestLand_PassesCorrectParametersToStore(t *testing.T) { } func TestLand_ReturnsErrorOnStorageFailure(t *testing.T) { - store := &mockStorage{ - requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return fmt.Errorf("database connection failed") - }, - }, - changeProviderStore: &mockChangeProviderStore{ - createFunc: func(ctx context.Context, changeProvider entity.ChangeProvider) error { - return nil - }, - }, - } + ctrl := gomock.NewController(t) + mockReqStore := storagemock.NewMockRequestStore(ctrl) + mockReqStore.EXPECT().Create(gomock.Any(), gomock.Any()).Return(fmt.Errorf("database connection failed")) + store := storagemock.NewMockStorage(ctrl) + store.EXPECT().GetRequestStore().Return(mockReqStore).AnyTimes() + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 1, nil }} @@ -328,18 +137,9 @@ func TestLand_ReturnsErrorOnStorageFailure(t *testing.T) { } func TestLand_ReturnsErrorOnCounterFailure(t *testing.T) { - store := &mockStorage{ - requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }, - changeProviderStore: &mockChangeProviderStore{ - createFunc: func(ctx context.Context, changeProvider entity.ChangeProvider) error { - return nil - }, - }, - } + ctrl := gomock.NewController(t) + store := storagemock.NewMockStorage(ctrl) + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 0, fmt.Errorf("counter unavailable") }} @@ -358,18 +158,12 @@ func TestLand_ReturnsErrorOnCounterFailure(t *testing.T) { func TestLand_CounterDomainIncludesQueue(t *testing.T) { var capturedDomain string - store := &mockStorage{ - requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }, - changeProviderStore: &mockChangeProviderStore{ - createFunc: func(ctx context.Context, changeProvider entity.ChangeProvider) error { - return nil - }, - }, - } + ctrl := gomock.NewController(t) + mockReqStore := storagemock.NewMockRequestStore(ctrl) + mockReqStore.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) + store := storagemock.NewMockStorage(ctrl) + store.EXPECT().GetRequestStore().Return(mockReqStore).AnyTimes() + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { capturedDomain = domain return 1, nil @@ -388,11 +182,9 @@ func TestLand_CounterDomainIncludesQueue(t *testing.T) { } func TestLand_ReturnsErrorOnEmptyQueue(t *testing.T) { - store := &mockStorage{requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }} + ctrl := gomock.NewController(t) + store := storagemock.NewMockStorage(ctrl) + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 1, nil }} @@ -410,11 +202,9 @@ func TestLand_ReturnsErrorOnEmptyQueue(t *testing.T) { } func TestLand_ReturnsErrorOnEmptyChangeUri(t *testing.T) { - store := &mockStorage{requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }} + ctrl := gomock.NewController(t) + store := storagemock.NewMockStorage(ctrl) + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 1, nil }} @@ -432,11 +222,9 @@ func TestLand_ReturnsErrorOnEmptyChangeUri(t *testing.T) { } func TestLand_ReturnsErrorOnNilChange(t *testing.T) { - store := &mockStorage{requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }} + ctrl := gomock.NewController(t) + store := storagemock.NewMockStorage(ctrl) + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 1, nil }} @@ -457,11 +245,12 @@ func TestLand_PublishesToQueue(t *testing.T) { var publishedTopic string var publishedMessage queue.Message - store := &mockStorage{requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }} + ctrl := gomock.NewController(t) + mockReqStore := storagemock.NewMockRequestStore(ctrl) + mockReqStore.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) + store := storagemock.NewMockStorage(ctrl) + store.EXPECT().GetRequestStore().Return(mockReqStore).AnyTimes() + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 123, nil }} @@ -501,11 +290,12 @@ func TestLand_PublishesToQueue(t *testing.T) { } func TestLand_ContinuesWhenPublishFails(t *testing.T) { - store := &mockStorage{requestStore: &mockRequestStore{ - createFunc: func(ctx context.Context, request entity.Request) error { - return nil - }, - }} + ctrl := gomock.NewController(t) + mockReqStore := storagemock.NewMockRequestStore(ctrl) + mockReqStore.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) + store := storagemock.NewMockStorage(ctrl) + store.EXPECT().GetRequestStore().Return(mockReqStore).AnyTimes() + cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) { return 999, nil }} From 62f47bd9f788ac47fc4528874d6edb690f46baa3 Mon Sep 17 00:00:00 2001 From: manjari Date: Wed, 25 Feb 2026 20:03:26 +0000 Subject: [PATCH 2/4] add gazelle ignore to storage mock BUILD.bazel --- extension/storage/mock/BUILD.bazel | 1 + 1 file changed, 1 insertion(+) diff --git a/extension/storage/mock/BUILD.bazel b/extension/storage/mock/BUILD.bazel index 1ff6df23..49da6756 100644 --- a/extension/storage/mock/BUILD.bazel +++ b/extension/storage/mock/BUILD.bazel @@ -66,6 +66,7 @@ gomock( source_importpath = "github.com/uber/submitqueue/extension/storage", ) +# gazelle:ignore go_library( name = "mock", srcs = [ From 2226415e9c658ee11ebc6e86ba9bd6a7eec36a3f Mon Sep 17 00:00:00 2001 From: manjari Date: Wed, 25 Feb 2026 20:10:27 +0000 Subject: [PATCH 3/4] add a readme for new store additions --- extension/storage/mock/README.md | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 extension/storage/mock/README.md diff --git a/extension/storage/mock/README.md b/extension/storage/mock/README.md new file mode 100644 index 00000000..61cf53df --- /dev/null +++ b/extension/storage/mock/README.md @@ -0,0 +1,42 @@ +# Storage Mocks + +Generated mocks for all `extension/storage` interfaces using [gomock](https://github.com/uber-go/mock). + +Mocks are **not checked in** — they are generated at build time by the Bazel `gomock` rule. + +## Adding a new store interface + +When a new store interface file is added to `extension/storage/`: + +1. Add a `//go:generate` directive to the new file: + ```go + //go:generate mockgen -source=new_store.go -destination=mock/new_store.go -package=mock + ``` + +2. Add the file to `exports_files` in `extension/storage/BUILD.bazel`. + +3. Add a new `gomock` rule in `extension/storage/mock/BUILD.bazel`: + ```starlark + gomock( + name = "mock_new_store_src", + out = "new_store_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:new_store.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", + ) + ``` + +4. Add the new rule target to the `go_library` srcs in the same file: + ```starlark + go_library( + name = "mock", + srcs = [ + ... + ":mock_new_store_src", + ], + ... + ) + ``` + +> **Note:** This BUILD.bazel uses `# gazelle:ignore`, so gazelle will not update it automatically. From 1d77cf8d33450661d69d174210bba52a0ef76cc7 Mon Sep 17 00:00:00 2001 From: manjari Date: Wed, 25 Feb 2026 22:26:44 +0000 Subject: [PATCH 4/4] add claude instructions for mock --- CLAUDE.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 9d7db96b..3b86bc85 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -184,6 +184,61 @@ make clean # Clean Bazel cache **Add new entity:** 1. Create `entity/{domain}/{entity}.go` with test file and `BUILD.bazel` +**Add gomock for an extension interface:** + +Mocks use the Bazel `gomock` rule from `@rules_go//extras:gomock.bzl` and are generated at build time (not checked in). See `extension/storage/mock/` for the canonical example. + +To add a mock for a new interface file in an existing mock package (e.g., `extension/storage/new_store.go`): + +1. Add a `//go:generate` directive to the interface file: + ```go + //go:generate mockgen -source=new_store.go -destination=mock/new_store.go -package=mock + ``` +2. Add the file to `exports_files` in the parent `BUILD.bazel` with visibility to the mock package: + ```starlark + exports_files( + ["new_store.go", ...], + visibility = ["//extension/storage/mock:__pkg__"], + ) + ``` +3. Add a `gomock` rule in the mock `BUILD.bazel`: + ```starlark + gomock( + name = "mock_new_store_src", + out = "new_store_mock.go", + mockgen_tool = _MOCKGEN, + package = "mock", + source = "//extension/storage:new_store.go", + source_importpath = "github.com/uber/submitqueue/extension/storage", + ) + ``` +4. Add the rule target to the `go_library` srcs in the same file. + +To create a mock package for a new extension (e.g., `extension/queue/mock/`): + +1. Create `extension/{ext}/mock/BUILD.bazel` with `gomock` rules, a `go_library`, and `# gazelle:ignore`. +2. Add `exports_files` to `extension/{ext}/BUILD.bazel` for each interface file. +3. Follow the same per-interface pattern as above. + +Mock `BUILD.bazel` files use `# gazelle:ignore` so `make gazelle` will not update them — they must be maintained manually. + +**Using mocks in tests:** +```go +import storagemock "github.com/uber/submitqueue/extension/storage/mock" + +ctrl := gomock.NewController(t) +mockStore := storagemock.NewMockRequestStore(ctrl) +mockStore.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) +``` + +Test `BUILD.bazel` deps: +```starlark +deps = [ + "//extension/storage/mock", + "@org_uber_go_mock//gomock", +] +``` + ### Testing - **Avoid asserting on error messages** — assert on error type or generic error.