Skip to content

Commit de7e380

Browse files
committed
feat(queuepause): Add an in-memory fake store for tests
1 parent da0c447 commit de7e380

4 files changed

Lines changed: 158 additions & 0 deletions

File tree

‎submitqueue/extension/queuepause/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ The store only reports state. The gateway and orchestrator enforce it, so every
1515
- `noop` never pauses a queue. It is the default when no pause state is configured.
1616
- `file` reads a YAML file listing the queues that have switches set, and re-reads it on every `Get`, so editing the file pauses or unpauses a queue without a restart. The file must exist and parse when the store is constructed, so a mistyped path fails at startup. A later read or parse failure is returned as an error and never treated as not paused. Unknown fields are rejected so a misspelled switch cannot silently leave a queue unpaused.
1717

18+
- `fake` is an in-memory store for tests. A test sets a queue's state, changes it while consumers run, and can make every read fail. It is never used in production.
19+
1820
A deployer can supply its own implementation backed by a config service.
1921

2022
## Entities
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
load("@rules_go//go:def.bzl", "go_library", "go_test")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["fake.go"],
6+
importpath = "github.com/uber/submitqueue/submitqueue/extension/queuepause/fake",
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//submitqueue/entity:go_default_library",
10+
"//submitqueue/extension/queuepause:go_default_library",
11+
],
12+
)
13+
14+
go_test(
15+
name = "go_default_test",
16+
srcs = ["fake_test.go"],
17+
embed = [":go_default_library"],
18+
deps = [
19+
"//submitqueue/entity:go_default_library",
20+
"@com_github_stretchr_testify//assert:go_default_library",
21+
"@com_github_stretchr_testify//require:go_default_library",
22+
],
23+
)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) 2026 Uber Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package fake provides an in-memory queuepause.Store for tests and examples.
16+
// Seed or change a queue's state with Set; a queue never set is not paused.
17+
// FailWith injects a read error on every Get to exercise the error path without
18+
// a real backend. Safe for concurrent use, so a test can change state while a
19+
// consumer is reading. It is intended for tests and examples only, never
20+
// production.
21+
package fake
22+
23+
import (
24+
"context"
25+
"sync"
26+
27+
"github.com/uber/submitqueue/submitqueue/entity"
28+
"github.com/uber/submitqueue/submitqueue/extension/queuepause"
29+
)
30+
31+
var _ queuepause.Store = (*Store)(nil)
32+
33+
// Store is a programmable in-memory queuepause.Store.
34+
type Store struct {
35+
mu sync.RWMutex
36+
states map[string]entity.QueuePause
37+
err error
38+
}
39+
40+
// New returns a Store under which no queue is paused.
41+
func New() *Store {
42+
return &Store{states: map[string]entity.QueuePause{}}
43+
}
44+
45+
// Set replaces the pause state of a queue. Setting the zero state unpauses it.
46+
func (s *Store) Set(queue string, state entity.QueuePause) *Store {
47+
s.mu.Lock()
48+
defer s.mu.Unlock()
49+
s.states[queue] = state
50+
return s
51+
}
52+
53+
// FailWith makes every Get return err. Passing nil restores normal reads.
54+
func (s *Store) FailWith(err error) *Store {
55+
s.mu.Lock()
56+
defer s.mu.Unlock()
57+
s.err = err
58+
return s
59+
}
60+
61+
// Get returns the state last set for the queue, or the zero state if none was.
62+
func (s *Store) Get(_ context.Context, queue string) (entity.QueuePause, error) {
63+
s.mu.RLock()
64+
defer s.mu.RUnlock()
65+
if s.err != nil {
66+
return entity.QueuePause{}, s.err
67+
}
68+
return s.states[queue], nil
69+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2026 Uber Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fake
16+
17+
import (
18+
"context"
19+
"errors"
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
"github.com/stretchr/testify/require"
24+
25+
"github.com/uber/submitqueue/submitqueue/entity"
26+
)
27+
28+
func TestGet(t *testing.T) {
29+
frozen := entity.QueuePause{FreezeProcessing: true, Reason: "incident"}
30+
tests := []struct {
31+
name string
32+
setup func(*Store)
33+
queue string
34+
want entity.QueuePause
35+
}{
36+
{name: "queue never set is not paused", setup: func(*Store) {}, queue: "q", want: entity.QueuePause{}},
37+
{name: "queue set is returned", setup: func(s *Store) { s.Set("q", frozen) }, queue: "q", want: frozen},
38+
{name: "other queue is unaffected", setup: func(s *Store) { s.Set("q", frozen) }, queue: "other", want: entity.QueuePause{}},
39+
{name: "setting the zero state unpauses", setup: func(s *Store) { s.Set("q", frozen).Set("q", entity.QueuePause{}) }, queue: "q", want: entity.QueuePause{}},
40+
}
41+
for _, tt := range tests {
42+
t.Run(tt.name, func(t *testing.T) {
43+
s := New()
44+
tt.setup(s)
45+
46+
got, err := s.Get(context.Background(), tt.queue)
47+
require.NoError(t, err)
48+
assert.Equal(t, tt.want, got)
49+
})
50+
}
51+
}
52+
53+
func TestFailWith(t *testing.T) {
54+
boom := errors.New("backend down")
55+
s := New().Set("q", entity.QueuePause{RejectLands: true}).FailWith(boom)
56+
57+
got, err := s.Get(context.Background(), "q")
58+
require.ErrorIs(t, err, boom)
59+
assert.Equal(t, entity.QueuePause{}, got)
60+
61+
got, err = s.FailWith(nil).Get(context.Background(), "q")
62+
require.NoError(t, err)
63+
assert.Equal(t, entity.QueuePause{RejectLands: true}, got)
64+
}

0 commit comments

Comments
 (0)