chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"data-terminal-cloud/internal/alerts"
|
||||
"data-terminal-cloud/internal/billing"
|
||||
"data-terminal-cloud/internal/device"
|
||||
"data-terminal-cloud/internal/idempotency"
|
||||
"data-terminal-cloud/internal/opsconfig"
|
||||
)
|
||||
|
||||
func TestAlertBillingAndOpsConfigErrorsHaveStableMappings(t *testing.T) {
|
||||
type errorWriter func(http.ResponseWriter, *http.Request, error)
|
||||
tests := []struct {
|
||||
name string
|
||||
writer errorWriter
|
||||
err error
|
||||
status int
|
||||
code string
|
||||
}{
|
||||
{"alert invalid", writeAlertError, alerts.ErrInvalid, http.StatusBadRequest, "VALIDATION_FAILED"},
|
||||
{"alert denied", writeAlertError, alerts.ErrAccessDenied, http.StatusForbidden, "OPS_ACCESS_DENIED"},
|
||||
{"alert missing", writeAlertError, alerts.ErrNotFound, http.StatusNotFound, "ALERT_NOT_FOUND"},
|
||||
{"alert version", writeAlertError, alerts.ErrVersionConflict, http.StatusPreconditionFailed, "VERSION_CONFLICT"},
|
||||
{"alert state", writeAlertError, alerts.ErrStateConflict, http.StatusConflict, "ALERT_STATE_CONFLICT"},
|
||||
{"alert key", writeAlertError, idempotency.ErrKeyConflict, http.StatusConflict, "IDEMPOTENCY_KEY_CONFLICT"},
|
||||
{"alert dependency", writeAlertError, errors.New("database unavailable"), http.StatusInternalServerError, "INTERNAL_ERROR"},
|
||||
{"billing invalid", writeBillingError, billing.ErrInvalid, http.StatusBadRequest, "VALIDATION_FAILED"},
|
||||
{"billing denied", writeBillingError, billing.ErrAccessDenied, http.StatusForbidden, "OPS_ACCESS_DENIED"},
|
||||
{"billing missing", writeBillingError, billing.ErrNotFound, http.StatusNotFound, "RESOURCE_NOT_FOUND"},
|
||||
{"billing version", writeBillingError, billing.ErrVersionConflict, http.StatusPreconditionFailed, "VERSION_CONFLICT"},
|
||||
{"billing self approval", writeBillingError, billing.ErrSelfApproval, http.StatusConflict, "SELF_APPROVAL_FORBIDDEN"},
|
||||
{"billing state", writeBillingError, billing.ErrStateConflict, http.StatusConflict, "BILLING_STATE_CONFLICT"},
|
||||
{"billing key", writeBillingError, idempotency.ErrKeyConflict, http.StatusConflict, "IDEMPOTENCY_KEY_CONFLICT"},
|
||||
{"billing in progress", writeBillingError, idempotency.ErrInProgress, http.StatusConflict, "IDEMPOTENCY_IN_PROGRESS"},
|
||||
{"billing dependency", writeBillingError, errors.New("database unavailable"), http.StatusInternalServerError, "INTERNAL_ERROR"},
|
||||
{"config invalid", writeOpsConfigError, opsconfig.ErrInvalid, http.StatusBadRequest, "VALIDATION_FAILED"},
|
||||
{"config denied", writeOpsConfigError, opsconfig.ErrAccessDenied, http.StatusForbidden, "OPS_ACCESS_DENIED"},
|
||||
{"config missing", writeOpsConfigError, opsconfig.ErrNotFound, http.StatusNotFound, "RESOURCE_NOT_FOUND"},
|
||||
{"config version", writeOpsConfigError, opsconfig.ErrVersionConflict, http.StatusPreconditionFailed, "VERSION_CONFLICT"},
|
||||
{"config self approval", writeOpsConfigError, opsconfig.ErrSelfApproval, http.StatusConflict, "SELF_APPROVAL_FORBIDDEN"},
|
||||
{"config state", writeOpsConfigError, opsconfig.ErrStateConflict, http.StatusConflict, "CONFIG_STATE_CONFLICT"},
|
||||
{"config gate", writeOpsConfigError, opsconfig.ErrPublishGate, http.StatusConflict, "CONFIG_PUBLISH_GATE_FAILED"},
|
||||
{"config key", writeOpsConfigError, idempotency.ErrKeyConflict, http.StatusConflict, "IDEMPOTENCY_KEY_CONFLICT"},
|
||||
{"config in progress", writeOpsConfigError, idempotency.ErrInProgress, http.StatusConflict, "IDEMPOTENCY_IN_PROGRESS"},
|
||||
{"config dependency", writeOpsConfigError, errors.New("database unavailable"), http.StatusInternalServerError, "INTERNAL_ERROR"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(http.MethodPost, "/api/v1/ops/test", nil)
|
||||
test.writer(recorder, request, test.err)
|
||||
if recorder.Code != test.status || !strings.Contains(recorder.Body.String(), test.code) {
|
||||
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTelemetryErrorsHaveStableMappings(t *testing.T) {
|
||||
tests := []struct {
|
||||
err error
|
||||
status int
|
||||
code string
|
||||
}{
|
||||
{&device.TelemetryConflictError{}, http.StatusConflict, "TELEMETRY_EVENT_CONFLICT"},
|
||||
{device.ErrTelemetryConfigAhead, http.StatusConflict, "CONFIG_VERSION_CONFLICT"},
|
||||
{device.ErrTelemetryConversationMissing, http.StatusNotFound, "TELEMETRY_CONVERSATION_NOT_FOUND"},
|
||||
{device.ErrTelemetryCommandConflict, http.StatusConflict, "DEVICE_COMMAND_STATE_CONFLICT"},
|
||||
{idempotency.ErrKeyConflict, http.StatusConflict, "IDEMPOTENCY_KEY_REUSED"},
|
||||
{idempotency.ErrInProgress, http.StatusConflict, "REQUEST_IN_PROGRESS"},
|
||||
{errors.New("database unavailable"), http.StatusServiceUnavailable, "DEVICE_TELEMETRY_UNAVAILABLE"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(http.MethodPost, "/device/v2/telemetry:batch", nil)
|
||||
writeTelemetryError(recorder, request, test.err)
|
||||
if recorder.Code != test.status || !strings.Contains(recorder.Body.String(), test.code) {
|
||||
t.Fatalf("error=%v status=%d body=%s", test.err, recorder.Code, recorder.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user