repos / pico

pico services mono repo
git clone https://github.com/picosh/pico.git

pico / pkg / apps / pipe
Eric Bower  ·  2025-12-26

topic_test.go

  1package pipe
  2
  3import (
  4	"testing"
  5)
  6
  7func TestToTopic(t *testing.T) {
  8	tests := []struct {
  9		name     string
 10		userName string
 11		topic    string
 12		want     string
 13	}{
 14		{
 15			name:     "basic topic",
 16			userName: "alice",
 17			topic:    "mytopic",
 18			want:     "alice/mytopic",
 19		},
 20		{
 21			name:     "already prefixed with username",
 22			userName: "alice",
 23			topic:    "alice/mytopic",
 24			want:     "alice/mytopic",
 25		},
 26		{
 27			name:     "different user prefix not stripped",
 28			userName: "alice",
 29			topic:    "bob/mytopic",
 30			want:     "alice/bob/mytopic",
 31		},
 32		{
 33			name:     "empty topic",
 34			userName: "alice",
 35			topic:    "",
 36			want:     "alice/",
 37		},
 38	}
 39
 40	for _, tt := range tests {
 41		t.Run(tt.name, func(t *testing.T) {
 42			got := toTopic(tt.userName, tt.topic)
 43			if got != tt.want {
 44				t.Errorf("toTopic(%q, %q) = %q, want %q", tt.userName, tt.topic, got, tt.want)
 45			}
 46		})
 47	}
 48}
 49
 50func TestToPublicTopic(t *testing.T) {
 51	tests := []struct {
 52		name  string
 53		topic string
 54		want  string
 55	}{
 56		{
 57			name:  "basic topic",
 58			topic: "mytopic",
 59			want:  "public/mytopic",
 60		},
 61		{
 62			name:  "already public prefixed",
 63			topic: "public/mytopic",
 64			want:  "public/mytopic",
 65		},
 66		{
 67			name:  "empty topic",
 68			topic: "",
 69			want:  "public/",
 70		},
 71	}
 72
 73	for _, tt := range tests {
 74		t.Run(tt.name, func(t *testing.T) {
 75			got := toPublicTopic(tt.topic)
 76			if got != tt.want {
 77				t.Errorf("toPublicTopic(%q) = %q, want %q", tt.topic, got, tt.want)
 78			}
 79		})
 80	}
 81}
 82
 83func TestParseArgList(t *testing.T) {
 84	tests := []struct {
 85		name string
 86		arg  string
 87		want []string
 88	}{
 89		{
 90			name: "single item",
 91			arg:  "alice",
 92			want: []string{"alice"},
 93		},
 94		{
 95			name: "multiple items",
 96			arg:  "alice,bob,charlie",
 97			want: []string{"alice", "bob", "charlie"},
 98		},
 99		{
100			name: "items with spaces",
101			arg:  "alice, bob , charlie",
102			want: []string{"alice", "bob", "charlie"},
103		},
104		{
105			name: "empty string",
106			arg:  "",
107			want: []string{""},
108		},
109	}
110
111	for _, tt := range tests {
112		t.Run(tt.name, func(t *testing.T) {
113			got := parseArgList(tt.arg)
114			if len(got) != len(tt.want) {
115				t.Errorf("parseArgList(%q) returned %d items, want %d", tt.arg, len(got), len(tt.want))
116				return
117			}
118			for i := range got {
119				if got[i] != tt.want[i] {
120					t.Errorf("parseArgList(%q)[%d] = %q, want %q", tt.arg, i, got[i], tt.want[i])
121				}
122			}
123		})
124	}
125}
126
127func TestResolveTopic(t *testing.T) {
128	tests := []struct {
129		name   string
130		input  TopicResolveInput
131		expect TopicResolveOutput
132	}{
133		{
134			name: "basic private topic",
135			input: TopicResolveInput{
136				UserName: "alice",
137				Topic:    "mytopic",
138				IsAdmin:  false,
139				IsPublic: false,
140			},
141			expect: TopicResolveOutput{
142				Name:        "alice/mytopic",
143				WithoutUser: "mytopic",
144			},
145		},
146		{
147			name: "public topic",
148			input: TopicResolveInput{
149				UserName: "alice",
150				Topic:    "mytopic",
151				IsAdmin:  false,
152				IsPublic: true,
153			},
154			expect: TopicResolveOutput{
155				Name:        "public/mytopic",
156				WithoutUser: "public/mytopic",
157			},
158		},
159		{
160			name: "admin with absolute path",
161			input: TopicResolveInput{
162				UserName: "admin",
163				Topic:    "/rawtopic",
164				IsAdmin:  true,
165				IsPublic: false,
166			},
167			expect: TopicResolveOutput{
168				Name:        "rawtopic",
169				WithoutUser: "",
170			},
171		},
172		{
173			name: "admin without absolute path treated as regular user",
174			input: TopicResolveInput{
175				UserName: "admin",
176				Topic:    "mytopic",
177				IsAdmin:  true,
178				IsPublic: false,
179			},
180			expect: TopicResolveOutput{
181				Name:        "admin/mytopic",
182				WithoutUser: "mytopic",
183			},
184		},
185		{
186			name: "topic already prefixed with username",
187			input: TopicResolveInput{
188				UserName: "alice",
189				Topic:    "alice/mytopic",
190				IsAdmin:  false,
191				IsPublic: false,
192			},
193			expect: TopicResolveOutput{
194				Name:        "alice/mytopic",
195				WithoutUser: "alice/mytopic",
196			},
197		},
198		{
199			name: "public topic already prefixed",
200			input: TopicResolveInput{
201				UserName: "alice",
202				Topic:    "public/mytopic",
203				IsAdmin:  false,
204				IsPublic: true,
205			},
206			expect: TopicResolveOutput{
207				Name:        "public/mytopic",
208				WithoutUser: "public/mytopic",
209			},
210		},
211		{
212			name: "access list exists - user has access",
213			input: TopicResolveInput{
214				UserName:           "bob",
215				Topic:              "sharedtopic",
216				IsAdmin:            false,
217				IsPublic:           false,
218				ExistingAccessList: []string{"bob", "charlie"},
219				HasExistingAccess:  true,
220				HasUserAccess:      true,
221			},
222			expect: TopicResolveOutput{
223				Name:        "sharedtopic",
224				WithoutUser: "sharedtopic",
225			},
226		},
227		{
228			name: "access list exists - user denied (private)",
229			input: TopicResolveInput{
230				UserName:           "eve",
231				Topic:              "sharedtopic",
232				IsAdmin:            false,
233				IsPublic:           false,
234				ExistingAccessList: []string{"bob", "charlie"},
235				HasExistingAccess:  true,
236				HasUserAccess:      false,
237			},
238			expect: TopicResolveOutput{
239				Name:        "eve/sharedtopic",
240				WithoutUser: "sharedtopic",
241			},
242		},
243		{
244			name: "access list exists - user denied (public) - generates new topic",
245			input: TopicResolveInput{
246				UserName:           "eve",
247				Topic:              "sharedtopic",
248				IsAdmin:            false,
249				IsPublic:           true,
250				ExistingAccessList: []string{"bob", "charlie"},
251				HasExistingAccess:  true,
252				HasUserAccess:      false,
253			},
254			expect: TopicResolveOutput{
255				Name:             "public/sharedtopic",
256				WithoutUser:      "public/sharedtopic",
257				AccessDenied:     true,
258				GenerateNewTopic: true,
259			},
260		},
261		{
262			name: "access list creator gets access",
263			input: TopicResolveInput{
264				UserName:           "alice",
265				Topic:              "newtopic",
266				IsAdmin:            false,
267				IsPublic:           false,
268				AccessList:         []string{"bob"},
269				ExistingAccessList: []string{"bob"},
270				HasExistingAccess:  true,
271				IsAccessCreator:    true,
272				HasUserAccess:      false,
273			},
274			expect: TopicResolveOutput{
275				Name:        "newtopic",
276				WithoutUser: "newtopic",
277			},
278		},
279		{
280			name: "admin bypasses access control",
281			input: TopicResolveInput{
282				UserName:           "admin",
283				Topic:              "restricted",
284				IsAdmin:            true,
285				IsPublic:           false,
286				ExistingAccessList: []string{"bob"},
287				HasExistingAccess:  true,
288				HasUserAccess:      false,
289			},
290			expect: TopicResolveOutput{
291				Name:        "admin/restricted",
292				WithoutUser: "restricted",
293			},
294		},
295	}
296
297	for _, tt := range tests {
298		t.Run(tt.name, func(t *testing.T) {
299			got := resolveTopic(tt.input)
300			if got.Name != tt.expect.Name {
301				t.Errorf("resolveTopic().Name = %q, want %q", got.Name, tt.expect.Name)
302			}
303			if got.WithoutUser != tt.expect.WithoutUser {
304				t.Errorf("resolveTopic().WithoutUser = %q, want %q", got.WithoutUser, tt.expect.WithoutUser)
305			}
306			if got.AccessDenied != tt.expect.AccessDenied {
307				t.Errorf("resolveTopic().AccessDenied = %v, want %v", got.AccessDenied, tt.expect.AccessDenied)
308			}
309			if got.GenerateNewTopic != tt.expect.GenerateNewTopic {
310				t.Errorf("resolveTopic().GenerateNewTopic = %v, want %v", got.GenerateNewTopic, tt.expect.GenerateNewTopic)
311			}
312		})
313	}
314}