@@ -412,3 +412,188 @@ func TestClientProjectAccessor(t *testing.T) {
412412 t .Errorf ("expected project %q, got %q" , testProject , c .Project ())
413413 }
414414}
415+
416+ // ---------------------------------------------------------------------------
417+ // ScheduledSession Runs
418+ // ---------------------------------------------------------------------------
419+
420+ func TestScheduledSessionRuns (t * testing.T ) {
421+ want := & types.SessionList {
422+ ListMeta : types.ListMeta {Kind : "SessionList" , Page : 1 , Size : 10 , Total : 2 },
423+ Items : []types.Session {
424+ {ObjectReference : types.ObjectReference {ID : "sess-run-1" }, Name : "run-1" },
425+ {ObjectReference : types.ObjectReference {ID : "sess-run-2" }, Name : "run-2" },
426+ },
427+ }
428+
429+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
430+ if r .Method != http .MethodGet {
431+ t .Errorf ("expected GET, got %s" , r .Method )
432+ }
433+ if ! strings .HasSuffix (r .URL .Path , "/scheduled-sessions/ss-runs/runs" ) {
434+ t .Errorf ("unexpected path: %s" , r .URL .Path )
435+ }
436+ if ! strings .Contains (r .URL .Path , "/projects/proj-a/" ) {
437+ t .Errorf ("expected project in path: %s" , r .URL .Path )
438+ }
439+ w .Header ().Set ("Content-Type" , "application/json" )
440+ w .WriteHeader (http .StatusOK )
441+ _ , _ = w .Write (marshalJSON (t , want ))
442+ }))
443+ defer srv .Close ()
444+
445+ c := newTestClient (t , srv )
446+ got , err := c .ScheduledSessions ().Runs (context .Background (), "proj-a" , "ss-runs" , & types.ListOptions {})
447+ if err != nil {
448+ t .Fatalf ("Runs: %v" , err )
449+ }
450+ if len (got .Items ) != 2 {
451+ t .Errorf ("expected 2 runs, got %d" , len (got .Items ))
452+ }
453+ if got .Items [0 ].ID != "sess-run-1" {
454+ t .Errorf ("unexpected first run: %+v" , got .Items [0 ])
455+ }
456+ }
457+
458+ // ---------------------------------------------------------------------------
459+ // streamingClient initialization
460+ // ---------------------------------------------------------------------------
461+
462+ func TestNewClient_StreamingClientCreated (t * testing.T ) {
463+ c , err := NewClient ("http://localhost:8080" , testToken , testProject )
464+ if err != nil {
465+ t .Fatalf ("NewClient: %v" , err )
466+ }
467+ if c .streamingClient == nil {
468+ t .Fatal ("expected streamingClient to be initialized" )
469+ }
470+ tr , ok := c .streamingClient .Transport .(* http.Transport )
471+ if ! ok {
472+ t .Fatal ("expected streamingClient transport to be *http.Transport" )
473+ }
474+ if ! tr .DisableCompression {
475+ t .Error ("expected streamingClient transport to have DisableCompression=true" )
476+ }
477+ }
478+
479+ // ---------------------------------------------------------------------------
480+ // WithInsecureSkipVerify applies to both clients
481+ // ---------------------------------------------------------------------------
482+
483+ func TestWithInsecureSkipVerify_BothClients (t * testing.T ) {
484+ c , err := NewClient ("http://localhost:8080" , testToken , testProject , WithInsecureSkipVerify ())
485+ if err != nil {
486+ t .Fatalf ("NewClient: %v" , err )
487+ }
488+ if ! c .insecureSkipVerify {
489+ t .Error ("expected insecureSkipVerify=true" )
490+ }
491+
492+ checkInsecure := func (name string , hc * http.Client ) {
493+ t .Helper ()
494+ tr , ok := hc .Transport .(* http.Transport )
495+ if ! ok || tr == nil {
496+ t .Fatalf ("%s: expected *http.Transport, got %T" , name , hc .Transport )
497+ }
498+ if tr .TLSClientConfig == nil {
499+ t .Fatalf ("%s: expected TLSClientConfig to be set" , name )
500+ }
501+ if ! tr .TLSClientConfig .InsecureSkipVerify {
502+ t .Errorf ("%s: expected InsecureSkipVerify=true" , name )
503+ }
504+ }
505+ checkInsecure ("httpClient" , c .httpClient )
506+ checkInsecure ("streamingClient" , c .streamingClient )
507+ }
508+
509+ // ---------------------------------------------------------------------------
510+ // ScheduledSessionPatch JSON round-trip
511+ // ---------------------------------------------------------------------------
512+
513+ func TestScheduledSessionPatch_JSONRoundTrip (t * testing.T ) {
514+ enabled := true
515+ timeout := int32 (3600 )
516+ inactivity := int32 (300 )
517+ stopOnRun := false
518+
519+ patch := types.ScheduledSessionPatch {
520+ AgentID : strPtr ("agent-1" ),
521+ Description : strPtr ("nightly build" ),
522+ Enabled : & enabled ,
523+ InactivityTimeout : & inactivity ,
524+ Name : strPtr ("nightly" ),
525+ RunnerType : strPtr ("claude" ),
526+ Schedule : strPtr ("0 2 * * *" ),
527+ SessionPrompt : strPtr ("run tests" ),
528+ StopOnRunFinished : & stopOnRun ,
529+ Timeout : & timeout ,
530+ Timezone : strPtr ("UTC" ),
531+ }
532+
533+ b , err := json .Marshal (patch )
534+ if err != nil {
535+ t .Fatalf ("marshal: %v" , err )
536+ }
537+
538+ var m map [string ]any
539+ if err := json .Unmarshal (b , & m ); err != nil {
540+ t .Fatalf ("unmarshal to map: %v" , err )
541+ }
542+
543+ expectedKeys := []string {
544+ "agent_id" , "description" , "enabled" , "inactivity_timeout" ,
545+ "name" , "runner_type" , "schedule" , "session_prompt" ,
546+ "stop_on_run_finished" , "timeout" , "timezone" ,
547+ }
548+ for _ , k := range expectedKeys {
549+ if _ , ok := m [k ]; ! ok {
550+ t .Errorf ("missing key %q in marshaled JSON" , k )
551+ }
552+ }
553+ if len (m ) != len (expectedKeys ) {
554+ t .Errorf ("expected %d keys, got %d: %v" , len (expectedKeys ), len (m ), m )
555+ }
556+
557+ // Verify false bool is NOT omitted (pointer semantics)
558+ if m ["stop_on_run_finished" ] != false {
559+ t .Errorf ("expected stop_on_run_finished=false, got %v" , m ["stop_on_run_finished" ])
560+ }
561+
562+ // Round-trip back to struct
563+ var decoded types.ScheduledSessionPatch
564+ if err := json .Unmarshal (b , & decoded ); err != nil {
565+ t .Fatalf ("unmarshal to struct: %v" , err )
566+ }
567+ if * decoded .AgentID != "agent-1" {
568+ t .Errorf ("agent_id mismatch: %v" , decoded .AgentID )
569+ }
570+ if * decoded .Timeout != 3600 {
571+ t .Errorf ("timeout mismatch: %v" , decoded .Timeout )
572+ }
573+ }
574+
575+ // Omitted fields should not appear in JSON
576+ func TestScheduledSessionPatch_OmitEmpty (t * testing.T ) {
577+ patch := types.ScheduledSessionPatch {
578+ Name : strPtr ("only-name" ),
579+ }
580+
581+ b , err := json .Marshal (patch )
582+ if err != nil {
583+ t .Fatalf ("marshal: %v" , err )
584+ }
585+
586+ var m map [string ]any
587+ if err := json .Unmarshal (b , & m ); err != nil {
588+ t .Fatalf ("unmarshal: %v" , err )
589+ }
590+
591+ if len (m ) != 1 {
592+ t .Errorf ("expected 1 key (name only), got %d: %v" , len (m ), m )
593+ }
594+ if m ["name" ] != "only-name" {
595+ t .Errorf ("expected name=only-name, got %v" , m ["name" ])
596+ }
597+ }
598+
599+ func strPtr (s string ) * string { return & s }
0 commit comments