Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/cli. OpenStatus CLI www.openstatus.dev
cli cli-app synthetic-monitoring
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323package terraform
import ( "context" "encoding/json" "fmt" "io" "net/http" "os" "strings" "testing")
type recordedCall struct { Procedure string Limit int32 Offset int32}
// fakeTransport serves a queue of bodies per procedure suffix. Procedures with no// queue get "{}", so unrelated sections of the export come back empty.type fakeTransport struct { bodies map[string][]fakeResponse calls []recordedCall}
type fakeResponse struct { status int body string}
func okResponse(body string) fakeResponse { return fakeResponse{status: http.StatusOK, body: body}}
// connectError builds a Connect error envelope for a unary JSON call. The HTTP// status is what maps back to a connect.Code on the client side.func connectError(status int, code string) fakeResponse { return fakeResponse{status: status, body: fmt.Sprintf(`{"code":%q,"message":"denied"}`, code)}}
func (f *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) { call := recordedCall{Procedure: req.URL.Path}
if req.Body != nil { raw, err := io.ReadAll(req.Body) if err != nil { return nil, err } var decoded struct { Limit int32 `json:"limit"` Offset int32 `json:"offset"` } if len(raw) > 0 { _ = json.Unmarshal(raw, &decoded) } call.Limit = decoded.Limit call.Offset = decoded.Offset } f.calls = append(f.calls, call)
resp := okResponse("{}") for suffix, queue := range f.bodies { if !strings.HasSuffix(req.URL.Path, suffix) { continue } if len(queue) == 0 { break } resp = queue[0] if len(queue) > 1 { f.bodies[suffix] = queue[1:] } break }
return &http.Response{ StatusCode: resp.status, Body: io.NopCloser(strings.NewReader(resp.body)), Header: http.Header{"Content-Type": []string{"application/json"}}, }, nil}
func (f *fakeTransport) callsTo(suffix string) []recordedCall { var out []recordedCall for _, c := range f.calls { if strings.HasSuffix(c.Procedure, suffix) { out = append(out, c) } } return out}
func newFetchClient(bodies map[string][]fakeResponse) (*http.Client, *fakeTransport) { transport := &fakeTransport{bodies: bodies} return &http.Client{Transport: transport}, transport}
func listBody(ids []string, totalSize int) string { entries := make([]string, 0, len(ids)) for _, id := range ids { entries = append(entries, fmt.Sprintf(`{"id":%q,"name":%q,"monitorCount":1}`, id, id)) } return fmt.Sprintf(`{"privateLocations":[%s],"totalSize":%d}`, strings.Join(entries, ","), totalSize)}
func getBody(id string, monitorIDs []string) string { quoted := make([]string, 0, len(monitorIDs)) for _, m := range monitorIDs { quoted = append(quoted, fmt.Sprintf("%q", m)) } return fmt.Sprintf(`{"privateLocation":{"id":%q,"name":%q,"monitorIds":[%s]}}`, id, id, strings.Join(quoted, ","))}
func manyIDs(prefix string, n int) []string { out := make([]string, 0, n) for i := range n { out = append(out, fmt.Sprintf("%s%d", prefix, i)) } return out}
func TestFetchPrivateLocations_SinglePage(t *testing.T) { client, transport := newFetchClient(map[string][]fakeResponse{ "/ListPrivateLocations": {okResponse(listBody([]string{"pl_1", "pl_2"}, 2))}, "/GetPrivateLocation": {okResponse(getBody("pl_1", []string{"mon-1", "mon-2"})), okResponse(getBody("pl_2", nil))}, })
data, err := FetchWorkspaceDataWithHTTPClient(context.Background(), client, "test-token") if err != nil { t.Fatalf("unexpected error: %v", err) }
if got := len(data.PrivateLocations); got != 2 { t.Fatalf("got %d private locations, want 2", got) } if got := data.PrivateLocations[0].GetMonitorIds(); len(got) != 2 { t.Errorf("got monitor_ids %v, want 2 entries", got) } if got := len(transport.callsTo("/ListPrivateLocations")); got != 1 { t.Errorf("got %d list calls, want 1", got) } if got := len(transport.callsTo("/GetPrivateLocation")); got != 2 { t.Errorf("got %d get calls, want 2", got) }}
func TestFetchPrivateLocations_MultiPage(t *testing.T) { firstPage := manyIDs("pl_", privateLocationPageSize) secondPage := manyIDs("pl_second_", 50)
client, transport := newFetchClient(map[string][]fakeResponse{ "/ListPrivateLocations": { okResponse(listBody(firstPage, 150)), okResponse(listBody(secondPage, 150)), }, "/GetPrivateLocation": {okResponse(getBody("pl_x", nil))}, })
data, err := FetchWorkspaceDataWithHTTPClient(context.Background(), client, "test-token") if err != nil { t.Fatalf("unexpected error: %v", err) }
if got := len(data.PrivateLocations); got != 150 { t.Fatalf("got %d private locations, want 150", got) }
listCalls := transport.callsTo("/ListPrivateLocations") if len(listCalls) != 2 { t.Fatalf("got %d list calls, want 2", len(listCalls)) } if listCalls[0].Limit != privateLocationPageSize || listCalls[0].Offset != 0 { t.Errorf("first page requested limit=%d offset=%d, want limit=%d offset=0", listCalls[0].Limit, listCalls[0].Offset, privateLocationPageSize) } if listCalls[1].Offset != privateLocationPageSize { t.Errorf("second page requested offset=%d, want %d", listCalls[1].Offset, privateLocationPageSize) }}
func TestFetchPrivateLocations_EmptyPageStopsLoop(t *testing.T) { client, transport := newFetchClient(map[string][]fakeResponse{ "/ListPrivateLocations": { okResponse(listBody([]string{"pl_1"}, 99)), okResponse(listBody(nil, 99)), }, "/GetPrivateLocation": {okResponse(getBody("pl_1", nil))}, })
data, err := FetchWorkspaceDataWithHTTPClient(context.Background(), client, "test-token") if err != nil { t.Fatalf("unexpected error: %v", err) }
if got := len(data.PrivateLocations); got != 1 { t.Fatalf("got %d private locations, want 1", got) } if got := len(transport.callsTo("/ListPrivateLocations")); got != 2 { t.Errorf("got %d list calls, want 2 (loop must stop on an empty page)", got) }}
func TestFetchPrivateLocations_EmptyWorkspace(t *testing.T) { client, transport := newFetchClient(map[string][]fakeResponse{ "/ListPrivateLocations": {okResponse(listBody(nil, 0))}, })
data, err := FetchWorkspaceDataWithHTTPClient(context.Background(), client, "test-token") if err != nil { t.Fatalf("unexpected error: %v", err) }
if len(data.PrivateLocations) != 0 { t.Errorf("got %d private locations, want 0", len(data.PrivateLocations)) } if got := len(transport.callsTo("/GetPrivateLocation")); got != 0 { t.Errorf("got %d get calls, want 0", got) }}
func TestFetchPrivateLocations_ToleratesUnavailableFeature(t *testing.T) { tests := []struct { name string status int code string procedure string }{ {"permission denied on list", http.StatusForbidden, "permission_denied", "/ListPrivateLocations"}, {"unimplemented on list", http.StatusNotFound, "unimplemented", "/ListPrivateLocations"}, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { client, _ := newFetchClient(map[string][]fakeResponse{ tt.procedure: {connectError(tt.status, tt.code)}, })
var data *WorkspaceData var err error warning := captureStderr(t, func() { data, err = FetchWorkspaceDataWithHTTPClient(context.Background(), client, "test-token") })
if err != nil { t.Fatalf("expected the export to continue, got error: %v", err) } if len(data.PrivateLocations) != 0 { t.Errorf("got %d private locations, want 0", len(data.PrivateLocations)) } if !strings.Contains(warning, "warning: skipping private locations") { t.Errorf("expected a stderr warning, got: %q", warning) } }) }}
func captureStderr(t *testing.T, fn func()) string { t.Helper() orig := os.Stderr r, w, err := os.Pipe() if err != nil { t.Fatalf("pipe: %v", err) } os.Stderr = w defer func() { os.Stderr = orig }()
done := make(chan string, 1) go func() { b, _ := io.ReadAll(r) done <- string(b) }()
fn() w.Close() return <-done}
func TestFetchPrivateLocations_FatalOnOtherErrors(t *testing.T) { client, _ := newFetchClient(map[string][]fakeResponse{ "/ListPrivateLocations": {connectError(http.StatusUnauthorized, "unauthenticated")}, })
if _, err := FetchWorkspaceDataWithHTTPClient(context.Background(), client, "test-token"); err == nil { t.Fatal("expected an error for a non-entitlement failure, got nil") }}
func TestFetchPrivateLocations_PartialResultsDiscarded(t *testing.T) { client, _ := newFetchClient(map[string][]fakeResponse{ "/ListPrivateLocations": {okResponse(listBody([]string{"pl_1", "pl_2"}, 2))}, "/GetPrivateLocation": { okResponse(getBody("pl_1", []string{"mon-1"})), connectError(http.StatusForbidden, "permission_denied"), }, })
data, err := FetchWorkspaceDataWithHTTPClient(context.Background(), client, "test-token") if err != nil { t.Fatalf("expected the export to continue, got error: %v", err) } if len(data.PrivateLocations) != 0 { t.Errorf("got %d private locations, want 0 — partial results must be discarded", len(data.PrivateLocations)) }}
func TestFetchICMPMonitors(t *testing.T) { client, _ := newFetchClient(map[string][]fakeResponse{ "/ListMonitors": {okResponse(`{"icmpMonitors":[{"id":"777","name":"Gateway Ping","uri":"8.8.8.8","periodicity":"PERIODICITY_1M","active":true}]}`)}, })
data, err := FetchWorkspaceDataWithHTTPClient(context.Background(), client, "test-token") if err != nil { t.Fatalf("unexpected error: %v", err) }
if got := len(data.ICMPMonitors); got != 1 { t.Fatalf("got %d ICMP monitors, want 1", got) } if got := data.ICMPMonitors[0].GetName(); got != "Gateway Ping" { t.Errorf("got name %q, want 'Gateway Ping'", got) }}