|
8 | 8 | "sort" |
9 | 9 | "strings" |
10 | 10 | "testing" |
| 11 | + "time" |
11 | 12 |
|
12 | 13 | "google.golang.org/api/calendar/v3" |
13 | 14 | "google.golang.org/api/option" |
@@ -73,6 +74,127 @@ func TestCalendarFreeBusyCmd_ResolvesCalendarName(t *testing.T) { |
73 | 74 | } |
74 | 75 | } |
75 | 76 |
|
| 77 | +func TestCalendarFreeBusyCmd_RFC3339RangeDoesNotFetchTimezone(t *testing.T) { |
| 78 | + var got struct { |
| 79 | + TimeMin string `json:"timeMin"` |
| 80 | + TimeMax string `json:"timeMax"` |
| 81 | + } |
| 82 | + var unexpected []string |
| 83 | + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 84 | + path := strings.TrimPrefix(r.URL.Path, "/calendar/v3") |
| 85 | + if r.Method == http.MethodPost && strings.Contains(path, "/freeBusy") { |
| 86 | + _ = json.NewDecoder(r.Body).Decode(&got) |
| 87 | + w.Header().Set("Content-Type", "application/json") |
| 88 | + _ = json.NewEncoder(w).Encode(map[string]any{ |
| 89 | + "calendars": map[string]any{ |
| 90 | + "[email protected]": map[ string] any{ "busy": [] map[ string] any{}}, |
| 91 | + }, |
| 92 | + }) |
| 93 | + return |
| 94 | + } |
| 95 | + |
| 96 | + unexpected = append(unexpected, r.Method+" "+path) |
| 97 | + http.NotFound(w, r) |
| 98 | + })) |
| 99 | + defer srv.Close() |
| 100 | + |
| 101 | + svc, err := calendar.NewService(context.Background(), |
| 102 | + option.WithoutAuthentication(), |
| 103 | + option.WithHTTPClient(srv.Client()), |
| 104 | + option.WithEndpoint(srv.URL+"/"), |
| 105 | + ) |
| 106 | + if err != nil { |
| 107 | + t.Fatalf("NewService: %v", err) |
| 108 | + } |
| 109 | + result := executeWithCalendarTestService(t, []string{ |
| 110 | + "--json", |
| 111 | + |
| 112 | + "calendar", "freebusy", |
| 113 | + |
| 114 | + "--from", "2026-01-10T00:00:00-08:00", |
| 115 | + "--to", "2026-01-11T00:00:00-08:00", |
| 116 | + }, svc) |
| 117 | + if result.err != nil { |
| 118 | + t.Fatalf("Execute: %v", result.err) |
| 119 | + } |
| 120 | + if len(unexpected) != 0 { |
| 121 | + t.Fatalf("unexpected pre-freebusy requests: %#v", unexpected) |
| 122 | + } |
| 123 | + if got.TimeMin != "2026-01-10T00:00:00-08:00" || got.TimeMax != "2026-01-11T00:00:00-08:00" { |
| 124 | + t.Fatalf("expected original RFC3339 bounds, got %q -> %q", got.TimeMin, got.TimeMax) |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +func TestCalendarFreeBusyCmd_RelativeRangePayload(t *testing.T) { |
| 129 | + var got struct { |
| 130 | + TimeMin string `json:"timeMin"` |
| 131 | + TimeMax string `json:"timeMax"` |
| 132 | + Items []struct { |
| 133 | + ID string `json:"id"` |
| 134 | + } `json:"items"` |
| 135 | + } |
| 136 | + srv := httptest.NewServer(withPrimaryCalendar(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 137 | + path := strings.TrimPrefix(r.URL.Path, "/calendar/v3") |
| 138 | + switch { |
| 139 | + case r.Method == http.MethodPost && strings.Contains(path, "/freeBusy"): |
| 140 | + _ = json.NewDecoder(r.Body).Decode(&got) |
| 141 | + w.Header().Set("Content-Type", "application/json") |
| 142 | + _ = json.NewEncoder(w).Encode(map[string]any{ |
| 143 | + "calendars": map[string]any{ |
| 144 | + "[email protected]": map[ string] any{ "busy": [] map[ string] any{}}, |
| 145 | + }, |
| 146 | + }) |
| 147 | + default: |
| 148 | + http.NotFound(w, r) |
| 149 | + } |
| 150 | + }))) |
| 151 | + defer srv.Close() |
| 152 | + |
| 153 | + svc, err := calendar.NewService(context.Background(), |
| 154 | + option.WithoutAuthentication(), |
| 155 | + option.WithHTTPClient(srv.Client()), |
| 156 | + option.WithEndpoint(srv.URL+"/"), |
| 157 | + ) |
| 158 | + if err != nil { |
| 159 | + t.Fatalf("NewService: %v", err) |
| 160 | + } |
| 161 | + result := executeWithCalendarTestService(t, []string{ |
| 162 | + "--json", |
| 163 | + |
| 164 | + "calendar", "freebusy", |
| 165 | + |
| 166 | + "--from", calendarExprToday, |
| 167 | + "--to", calendarExprTomorrow, |
| 168 | + }, svc) |
| 169 | + if result.err != nil { |
| 170 | + t.Fatalf("Execute: %v", result.err) |
| 171 | + } |
| 172 | + |
| 173 | + from, err := time.Parse(time.RFC3339, got.TimeMin) |
| 174 | + if err != nil { |
| 175 | + t.Fatalf("timeMin is not RFC3339: %q: %v", got.TimeMin, err) |
| 176 | + } |
| 177 | + to, err := time.Parse(time.RFC3339, got.TimeMax) |
| 178 | + if err != nil { |
| 179 | + t.Fatalf("timeMax is not RFC3339: %q: %v", got.TimeMax, err) |
| 180 | + } |
| 181 | + if got.TimeMin == calendarExprToday || got.TimeMax == calendarExprTomorrow { |
| 182 | + t.Fatalf("expected parsed RFC3339 values, got timeMin=%q timeMax=%q", got.TimeMin, got.TimeMax) |
| 183 | + } |
| 184 | + if from.Location() != time.UTC || to.Location() != time.UTC { |
| 185 | + t.Fatalf("expected UTC payload values, got %v -> %v", from.Location(), to.Location()) |
| 186 | + } |
| 187 | + if from.Hour() != 0 || from.Minute() != 0 || from.Second() != 0 || to.Hour() != 0 || to.Minute() != 0 || to.Second() != 0 { |
| 188 | + t.Fatalf("expected whole-day bounds, got %s -> %s", got.TimeMin, got.TimeMax) |
| 189 | + } |
| 190 | + if to.Sub(from) != 48*time.Hour { |
| 191 | + t.Fatalf("expected today through tomorrow range, got %s -> %s", got.TimeMin, got.TimeMax) |
| 192 | + } |
| 193 | + if len( got. Items) != 1 || got. Items[ 0]. ID != "[email protected]" { |
| 194 | + t.Fatalf("expected work calendar item, got %#v", got.Items) |
| 195 | + } |
| 196 | +} |
| 197 | + |
76 | 198 | func TestCalendarConflictsCmd_AllCalendarsSelection(t *testing.T) { |
77 | 199 | var gotIDs []string |
78 | 200 | srv := httptest.NewServer(withPrimaryCalendar(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
0 commit comments