@@ -375,6 +375,238 @@ func TestCreate_NoEventIdReturned(t *testing.T) {
375375 }
376376}
377377
378+ func TestCreate_CreateEvent_InvalidParamsWithDetail (t * testing.T ) {
379+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
380+
381+ reg .Register (& httpmock.Stub {
382+ Method : "POST" ,
383+ URL : "/open-apis/calendar/v4/calendars/cal_test123/events" ,
384+ Body : map [string ]interface {}{
385+ "code" : errCodeInvalidParamsWithDetail ,
386+ "msg" : "invalid params" ,
387+ "error" : map [string ]interface {}{
388+ "details" : []interface {}{
389+ map [string ]interface {}{"value" : "end_time should be later than start_time" },
390+ },
391+ },
392+ },
393+ })
394+
395+ err := mountAndRun (t , CalendarCreate , []string {
396+ "+create" ,
397+ "--summary" , "Bad Time" ,
398+ "--start" , "2025-03-21T10:00:00+08:00" ,
399+ "--end" , "2025-03-21T11:00:00+08:00" ,
400+ "--calendar-id" , "cal_test123" ,
401+ "--as" , "bot" ,
402+ }, f , nil )
403+
404+ if err == nil {
405+ t .Fatal ("expected error for 190014, got nil" )
406+ }
407+ var exitErr * output.ExitError
408+ if ! errors .As (err , & exitErr ) {
409+ t .Fatalf ("expected *output.ExitError, got %T" , err )
410+ }
411+ if exitErr .Detail .Code != errCodeInvalidParamsWithDetail {
412+ t .Errorf ("expected code %d, got %d" , errCodeInvalidParamsWithDetail , exitErr .Detail .Code )
413+ }
414+ if ! strings .Contains (exitErr .Detail .Message , "end_time should be later than start_time" ) {
415+ t .Errorf ("expected detail value in message, got %q" , exitErr .Detail .Message )
416+ }
417+ }
418+
419+ func TestCreate_CreateEvent_InvalidParamsWithoutDetailValue (t * testing.T ) {
420+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
421+
422+ reg .Register (& httpmock.Stub {
423+ Method : "POST" ,
424+ URL : "/open-apis/calendar/v4/calendars/cal_test123/events" ,
425+ Body : map [string ]interface {}{
426+ "code" : errCodeInvalidParamsWithDetail ,
427+ "msg" : "invalid params" ,
428+ },
429+ })
430+
431+ err := mountAndRun (t , CalendarCreate , []string {
432+ "+create" ,
433+ "--summary" , "Bad Time" ,
434+ "--start" , "2025-03-21T10:00:00+08:00" ,
435+ "--end" , "2025-03-21T11:00:00+08:00" ,
436+ "--calendar-id" , "cal_test123" ,
437+ "--as" , "bot" ,
438+ }, f , nil )
439+
440+ if err == nil {
441+ t .Fatal ("expected error for 190014, got nil" )
442+ }
443+ var exitErr * output.ExitError
444+ if ! errors .As (err , & exitErr ) {
445+ t .Fatalf ("expected *output.ExitError, got %T" , err )
446+ }
447+ if exitErr .Detail .Code != errCodeInvalidParamsWithDetail {
448+ t .Errorf ("expected code %d, got %d" , errCodeInvalidParamsWithDetail , exitErr .Detail .Code )
449+ }
450+ }
451+
452+ func TestCreate_CreateEvent_InvalidParams_ErrorNotMap (t * testing.T ) {
453+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
454+
455+ reg .Register (& httpmock.Stub {
456+ Method : "POST" ,
457+ URL : "/open-apis/calendar/v4/calendars/cal_test123/events" ,
458+ RawBody : []byte (`{"code":190014,"msg":"invalid params","error":"just a string"}` ),
459+ ContentType : "text/plain" ,
460+ })
461+
462+ err := mountAndRun (t , CalendarCreate , []string {
463+ "+create" ,
464+ "--summary" , "Bad Time" ,
465+ "--start" , "2025-03-21T10:00:00+08:00" ,
466+ "--end" , "2025-03-21T11:00:00+08:00" ,
467+ "--calendar-id" , "cal_test123" ,
468+ "--as" , "bot" ,
469+ }, f , nil )
470+
471+ if err == nil {
472+ t .Fatal ("expected error for 190014, got nil" )
473+ }
474+ var exitErr * output.ExitError
475+ if ! errors .As (err , & exitErr ) {
476+ t .Fatalf ("expected *output.ExitError, got %T" , err )
477+ }
478+ if exitErr .Detail .Code != errCodeInvalidParamsWithDetail {
479+ t .Errorf ("expected code %d, got %d" , errCodeInvalidParamsWithDetail , exitErr .Detail .Code )
480+ }
481+ }
482+
483+ func TestCreate_CreateEvent_InvalidParams_NoDetailsKey (t * testing.T ) {
484+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
485+
486+ reg .Register (& httpmock.Stub {
487+ Method : "POST" ,
488+ URL : "/open-apis/calendar/v4/calendars/cal_test123/events" ,
489+ Body : map [string ]interface {}{
490+ "code" : errCodeInvalidParamsWithDetail ,
491+ "msg" : "invalid params" ,
492+ "error" : map [string ]interface {}{
493+ "other_key" : "no details here" ,
494+ },
495+ },
496+ })
497+
498+ err := mountAndRun (t , CalendarCreate , []string {
499+ "+create" ,
500+ "--summary" , "Bad Time" ,
501+ "--start" , "2025-03-21T10:00:00+08:00" ,
502+ "--end" , "2025-03-21T11:00:00+08:00" ,
503+ "--calendar-id" , "cal_test123" ,
504+ "--as" , "bot" ,
505+ }, f , nil )
506+
507+ if err == nil {
508+ t .Fatal ("expected error for 190014, got nil" )
509+ }
510+ var exitErr * output.ExitError
511+ if ! errors .As (err , & exitErr ) {
512+ t .Fatalf ("expected *output.ExitError, got %T" , err )
513+ }
514+ if exitErr .Detail .Code != errCodeInvalidParamsWithDetail {
515+ t .Errorf ("expected code %d, got %d" , errCodeInvalidParamsWithDetail , exitErr .Detail .Code )
516+ }
517+ }
518+
519+ func TestCreate_CreateEvent_InvalidParams_DetailItemNotMap (t * testing.T ) {
520+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
521+
522+ reg .Register (& httpmock.Stub {
523+ Method : "POST" ,
524+ URL : "/open-apis/calendar/v4/calendars/cal_test123/events" ,
525+ Body : map [string ]interface {}{
526+ "code" : errCodeInvalidParamsWithDetail ,
527+ "msg" : "invalid params" ,
528+ "error" : map [string ]interface {}{
529+ "details" : []interface {}{nil },
530+ },
531+ },
532+ })
533+
534+ err := mountAndRun (t , CalendarCreate , []string {
535+ "+create" ,
536+ "--summary" , "Bad Time" ,
537+ "--start" , "2025-03-21T10:00:00+08:00" ,
538+ "--end" , "2025-03-21T11:00:00+08:00" ,
539+ "--calendar-id" , "cal_test123" ,
540+ "--as" , "bot" ,
541+ }, f , nil )
542+
543+ if err == nil {
544+ t .Fatal ("expected error for 190014, got nil" )
545+ }
546+ var exitErr * output.ExitError
547+ if ! errors .As (err , & exitErr ) {
548+ t .Fatalf ("expected *output.ExitError, got %T" , err )
549+ }
550+ if exitErr .Detail .Code != errCodeInvalidParamsWithDetail {
551+ t .Errorf ("expected code %d, got %d" , errCodeInvalidParamsWithDetail , exitErr .Detail .Code )
552+ }
553+ }
554+
555+ func TestCreate_WithAttendees_InvalidParamsWithDetail_RollsBack (t * testing.T ) {
556+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
557+
558+ reg .Register (& httpmock.Stub {
559+ Method : "POST" ,
560+ URL : "/open-apis/calendar/v4/calendars/cal_test123/events" ,
561+ Body : map [string ]interface {}{
562+ "code" : 0 , "msg" : "ok" ,
563+ "data" : map [string ]interface {}{
564+ "event" : map [string ]interface {}{
565+ "event_id" : "evt_190014" ,
566+ "summary" : "Bad Attendees" ,
567+ "start_time" : map [string ]interface {}{"timestamp" : "1742515200" },
568+ "end_time" : map [string ]interface {}{"timestamp" : "1742518800" },
569+ },
570+ },
571+ },
572+ })
573+ reg .Register (& httpmock.Stub {
574+ Method : "POST" ,
575+ URL : "/events/evt_190014/attendees" ,
576+ Body : map [string ]interface {}{
577+ "code" : errCodeInvalidParamsWithDetail ,
578+ "msg" : "invalid params" ,
579+ "error" : map [string ]interface {}{
580+ "details" : []interface {}{
581+ map [string ]interface {}{"value" : "invalid attendee open_id" },
582+ },
583+ },
584+ },
585+ })
586+ reg .Register (& httpmock.Stub {
587+ Method : "DELETE" ,
588+ URL : "/events/evt_190014" ,
589+ Body : map [string ]interface {}{"code" : 0 , "msg" : "ok" },
590+ })
591+
592+ err := mountAndRun (t , CalendarCreate , []string {
593+ "+create" ,
594+ "--summary" , "Bad Attendees" ,
595+ "--start" , "2025-03-21T00:00:00+08:00" ,
596+ "--end" , "2025-03-21T01:00:00+08:00" ,
597+ "--calendar-id" , "cal_test123" ,
598+ "--attendee-ids" , "ou_invalid" ,
599+ "--as" , "bot" ,
600+ }, f , nil )
601+
602+ if err == nil {
603+ t .Fatal ("expected error for invalid attendees with 190014, got nil" )
604+ }
605+ if ! strings .Contains (err .Error (), "invalid attendee open_id" ) {
606+ t .Errorf ("expected detail value in error, got: %v" , err )
607+ }
608+ }
609+
378610// ---------------------------------------------------------------------------
379611// CalendarAgenda tests
380612// ---------------------------------------------------------------------------
@@ -645,6 +877,67 @@ func TestAgenda_ExplicitCalendarId(t *testing.T) {
645877 }
646878}
647879
880+ func TestAgenda_InvalidParamsWithDetail (t * testing.T ) {
881+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
882+
883+ reg .Register (& httpmock.Stub {
884+ Method : "GET" ,
885+ URL : "/events/instance_view" ,
886+ Body : map [string ]interface {}{
887+ "code" : errCodeInvalidParamsWithDetail ,
888+ "msg" : "invalid params" ,
889+ "error" : map [string ]interface {}{
890+ "details" : []interface {}{
891+ map [string ]interface {}{"value" : "start_time is required" },
892+ },
893+ },
894+ },
895+ })
896+
897+ err := mountAndRun (t , CalendarAgenda , []string {
898+ "+agenda" ,
899+ "--start" , "2025-03-21" ,
900+ "--end" , "2025-03-21" ,
901+ "--as" , "bot" ,
902+ }, f , nil )
903+
904+ if err == nil {
905+ t .Fatal ("expected error for 190014, got nil" )
906+ }
907+ var exitErr * output.ExitError
908+ if ! errors .As (err , & exitErr ) {
909+ t .Fatalf ("expected *output.ExitError, got %T" , err )
910+ }
911+ if exitErr .Detail .Code != errCodeInvalidParamsWithDetail {
912+ t .Errorf ("expected code %d, got %d" , errCodeInvalidParamsWithDetail , exitErr .Detail .Code )
913+ }
914+ }
915+
916+ func TestAgenda_NonExitError_Passthrough (t * testing.T ) {
917+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
918+
919+ reg .Register (& httpmock.Stub {
920+ Method : "GET" ,
921+ URL : "/events/instance_view" ,
922+ RawBody : []byte ("this is not json" ),
923+ })
924+
925+ err := mountAndRun (t , CalendarAgenda , []string {
926+ "+agenda" ,
927+ "--start" , "2025-03-21" ,
928+ "--end" , "2025-03-21" ,
929+ "--as" , "bot" ,
930+ }, f , nil )
931+
932+ if err == nil {
933+ t .Fatal ("expected error for non-JSON response, got nil" )
934+ }
935+ var exitErr * output.ExitError
936+ if errors .As (err , & exitErr ) && exitErr .Detail != nil && exitErr .Detail .Code != 0 {
937+ t .Fatalf ("expected non-API error passthrough, got API error code %d" , exitErr .Detail .Code )
938+ }
939+ }
940+
648941// ---------------------------------------------------------------------------
649942// CalendarFreebusy tests
650943// ---------------------------------------------------------------------------
@@ -725,6 +1018,46 @@ func TestFreebusy_APIError(t *testing.T) {
7251018 }
7261019}
7271020
1021+ func TestFreebusy_InvalidParamsWithDetail (t * testing.T ) {
1022+ f , _ , _ , reg := cmdutil .TestFactory (t , defaultConfig ())
1023+
1024+ reg .Register (& httpmock.Stub {
1025+ Method : "POST" ,
1026+ URL : "/open-apis/calendar/v4/freebusy/list" ,
1027+ Body : map [string ]interface {}{
1028+ "code" : errCodeInvalidParamsWithDetail ,
1029+ "msg" : "invalid params" ,
1030+ "error" : map [string ]interface {}{
1031+ "details" : []interface {}{
1032+ map [string ]interface {}{"value" : "user_id is invalid" },
1033+ },
1034+ },
1035+ },
1036+ })
1037+
1038+ err := mountAndRun (t , CalendarFreebusy , []string {
1039+ "+freebusy" ,
1040+ "--start" , "2025-03-21" ,
1041+ "--end" , "2025-03-21" ,
1042+ "--user-id" , "ou_someone" ,
1043+ "--as" , "bot" ,
1044+ }, f , nil )
1045+
1046+ if err == nil {
1047+ t .Fatal ("expected error for 190014, got nil" )
1048+ }
1049+ var exitErr * output.ExitError
1050+ if ! errors .As (err , & exitErr ) {
1051+ t .Fatalf ("expected *output.ExitError, got %T" , err )
1052+ }
1053+ if exitErr .Detail .Code != errCodeInvalidParamsWithDetail {
1054+ t .Errorf ("expected code %d, got %d" , errCodeInvalidParamsWithDetail , exitErr .Detail .Code )
1055+ }
1056+ if ! strings .Contains (exitErr .Detail .Message , "user_id is invalid" ) {
1057+ t .Errorf ("expected detail value in message, got %q" , exitErr .Detail .Message )
1058+ }
1059+ }
1060+
7281061// ---------------------------------------------------------------------------
7291062// CalendarSuggestion tests
7301063// ---------------------------------------------------------------------------
0 commit comments