@@ -160,3 +160,202 @@ func TestOperationRegistry_Retrieve(t *testing.T) {
160160 })
161161 }
162162}
163+
164+ func TestRegisterOperation (t * testing.T ) {
165+ t .Parallel ()
166+
167+ op1 := NewOperation (
168+ "test-op-1" ,
169+ semver .MustParse ("1.0.0" ),
170+ "Operation 1" ,
171+ func (e Bundle , deps OpDeps , input string ) (string , error ) { return input , nil },
172+ )
173+ op2 := NewOperation (
174+ "test-op-2" ,
175+ semver .MustParse ("2.0.0" ),
176+ "Operation 2" ,
177+ func (e Bundle , deps OpDeps , input int ) (int , error ) { return input * 2 , nil },
178+ )
179+
180+ t .Run ("register single operation" , func (t * testing.T ) {
181+ t .Parallel ()
182+
183+ registry := NewOperationRegistry ()
184+ RegisterOperation (registry , op1 )
185+
186+ retrievedOp , err := registry .Retrieve (op1 .Def ())
187+ require .NoError (t , err )
188+ assert .Equal (t , "test-op-1" , retrievedOp .ID ())
189+ assert .Equal (t , "1.0.0" , retrievedOp .Version ())
190+ })
191+
192+ t .Run ("register multiple operations with different types" , func (t * testing.T ) {
193+ t .Parallel ()
194+
195+ registry := NewOperationRegistry ()
196+ // Register operations separately since they have different type parameters
197+ RegisterOperation (registry , op1 )
198+ RegisterOperation (registry , op2 )
199+
200+ retrievedOp1 , err := registry .Retrieve (op1 .Def ())
201+ require .NoError (t , err )
202+ assert .Equal (t , "test-op-1" , retrievedOp1 .ID ())
203+
204+ retrievedOp2 , err := registry .Retrieve (op2 .Def ())
205+ require .NoError (t , err )
206+ assert .Equal (t , "test-op-2" , retrievedOp2 .ID ())
207+ })
208+
209+ t .Run ("overwrite existing operation" , func (t * testing.T ) {
210+ t .Parallel ()
211+
212+ op1Updated := NewOperation (
213+ "test-op-1" ,
214+ semver .MustParse ("1.0.0" ),
215+ "Operation 1 Updated" ,
216+ func (e Bundle , deps OpDeps , input string ) (string , error ) { return input + "-updated" , nil },
217+ )
218+
219+ registry := NewOperationRegistry ()
220+ RegisterOperation (registry , op1 )
221+ RegisterOperation (registry , op1Updated )
222+
223+ retrievedOp , err := registry .Retrieve (op1 .Def ())
224+ require .NoError (t , err )
225+ assert .Equal (t , "Operation 1 Updated" , retrievedOp .Description ())
226+ })
227+ }
228+
229+ func TestRegisterOperationRelaxed (t * testing.T ) {
230+ t .Parallel ()
231+
232+ type TestInput struct {
233+ A int `json:"a"`
234+ B int `json:"b"`
235+ }
236+
237+ op1 := NewOperation (
238+ "sum-op" ,
239+ semver .MustParse ("1.0.0" ),
240+ "Sum operation with struct input" ,
241+ func (e Bundle , deps OpDeps , input TestInput ) (int , error ) {
242+ return input .A + input .B , nil
243+ },
244+ )
245+
246+ op2 := NewOperation (
247+ "multiply-op" ,
248+ semver .MustParse ("1.0.0" ),
249+ "Multiply operation" ,
250+ func (e Bundle , deps OpDeps , input int ) (int , error ) {
251+ return input * 2 , nil
252+ },
253+ )
254+
255+ t .Run ("register and execute with map input from YAML" , func (t * testing.T ) {
256+ t .Parallel ()
257+
258+ registry := NewOperationRegistry ()
259+ RegisterOperationRelaxed (registry , op1 )
260+
261+ retrievedOp , err := registry .Retrieve (op1 .Def ())
262+ require .NoError (t , err )
263+
264+ // Simulate input from YAML unmarshaling
265+ yamlInput := map [string ]any {
266+ "a" : 10 ,
267+ "b" : 20 ,
268+ }
269+
270+ bundle := NewBundle (context .Background , logger .Nop (), nil )
271+ result , err := retrievedOp .handler (bundle , OpDeps {}, yamlInput )
272+ require .NoError (t , err )
273+ assert .Equal (t , 30 , result )
274+ })
275+
276+ t .Run ("register multiple operations with relaxed typing" , func (t * testing.T ) {
277+ t .Parallel ()
278+
279+ registry := NewOperationRegistry ()
280+ // Register operations separately since they have different type parameters
281+ RegisterOperationRelaxed (registry , op1 )
282+ RegisterOperationRelaxed (registry , op2 )
283+
284+ // Verify both operations are registered
285+ retrievedOp1 , err := registry .Retrieve (op1 .Def ())
286+ require .NoError (t , err )
287+ assert .Equal (t , "sum-op" , retrievedOp1 .ID ())
288+
289+ retrievedOp2 , err := registry .Retrieve (op2 .Def ())
290+ require .NoError (t , err )
291+ assert .Equal (t , "multiply-op" , retrievedOp2 .ID ())
292+ })
293+
294+ t .Run ("overwrite operation with relaxed version" , func (t * testing.T ) {
295+ t .Parallel ()
296+
297+ op1Strict := NewOperation (
298+ "sum-op" ,
299+ semver .MustParse ("1.0.0" ),
300+ "Sum operation strict" ,
301+ func (e Bundle , deps OpDeps , input TestInput ) (int , error ) {
302+ return input .A + input .B + 1 , nil
303+ },
304+ )
305+
306+ registry := NewOperationRegistry ()
307+ RegisterOperation (registry , op1Strict )
308+ RegisterOperationRelaxed (registry , op1 ) // Should overwrite
309+
310+ retrievedOp , err := registry .Retrieve (op1 .Def ())
311+ require .NoError (t , err )
312+
313+ // Use map input which would fail with strict version
314+ yamlInput := map [string ]any {
315+ "a" : 10 ,
316+ "b" : 20 ,
317+ }
318+
319+ bundle := NewBundle (context .Background , logger .Nop (), nil )
320+ result , err := retrievedOp .handler (bundle , OpDeps {}, yamlInput )
321+ require .NoError (t , err )
322+ // If it was the strict version, result would be 31
323+ assert .Equal (t , 30 , result )
324+ })
325+
326+ t .Run ("handle type conversion errors gracefully" , func (t * testing.T ) {
327+ t .Parallel ()
328+
329+ registry := NewOperationRegistry ()
330+ RegisterOperationRelaxed (registry , op1 )
331+
332+ retrievedOp , err := registry .Retrieve (op1 .Def ())
333+ require .NoError (t , err )
334+
335+ // Provide input that cannot be converted
336+ invalidInput := "not a struct"
337+
338+ bundle := NewBundle (context .Background , logger .Nop (), nil )
339+ _ , err = retrievedOp .handler (bundle , OpDeps {}, invalidInput )
340+ require .Error (t , err )
341+ assert .Contains (t , err .Error (), "input type mismatch" )
342+ })
343+
344+ t .Run ("execute operation with direct struct input" , func (t * testing.T ) {
345+ t .Parallel ()
346+
347+ registry := NewOperationRegistry ()
348+ RegisterOperationRelaxed (registry , op1 )
349+
350+ retrievedOp , err := registry .Retrieve (op1 .Def ())
351+ require .NoError (t , err )
352+
353+ // Even with relaxed typing, direct struct input should work
354+ directInput := TestInput {A : 5 , B : 15 }
355+
356+ bundle := NewBundle (context .Background , logger .Nop (), nil )
357+ result , err := retrievedOp .handler (bundle , OpDeps {}, directInput )
358+ require .NoError (t , err )
359+ assert .Equal (t , 20 , result )
360+ })
361+ }
0 commit comments