119 lines
4.8 KiB
Go
119 lines
4.8 KiB
Go
package openplatform
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDocsRoutesServeOpenAPIAndBothDocumentationViews(t *testing.T) {
|
|
handler := WithDocs(http.NotFoundHandler())
|
|
tests := []struct {
|
|
path string
|
|
contentType string
|
|
want string
|
|
}{
|
|
{OpenAPISpecPath, "application/yaml", "openapi: 3.0.3"},
|
|
{SwaggerUIPath, "text/html", "swagger-ui-bundle.js"},
|
|
{SimpleDocsPath, "text/html", "车辆数据开放平台"},
|
|
{swaggerInitJSPath, "application/javascript", `persistAuthorization: false`},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.path, func(t *testing.T) {
|
|
recorder := httptest.NewRecorder()
|
|
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, test.path, nil))
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
if !strings.HasPrefix(recorder.Header().Get("Content-Type"), test.contentType) {
|
|
t.Fatalf("content-type=%q", recorder.Header().Get("Content-Type"))
|
|
}
|
|
if !strings.Contains(recorder.Body.String(), test.want) {
|
|
t.Fatalf("body does not contain %q", test.want)
|
|
}
|
|
if recorder.Header().Get("X-Content-Type-Options") != "nosniff" {
|
|
t.Fatal("documentation asset must set nosniff")
|
|
}
|
|
if test.path == SimpleDocsPath && !strings.Contains(recorder.Header().Get("Content-Security-Policy"), "script-src 'unsafe-inline'") {
|
|
t.Fatal("simple documentation must allow its bundled interactive field renderer")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDocsRedirectsAndRejectsMutatingMethods(t *testing.T) {
|
|
handler := WithDocs(http.NotFoundHandler())
|
|
redirect := httptest.NewRecorder()
|
|
handler.ServeHTTP(redirect, httptest.NewRequest(http.MethodGet, "/open-api/docs", nil))
|
|
if redirect.Code != http.StatusPermanentRedirect || redirect.Header().Get("Location") != SimpleDocsPath {
|
|
t.Fatalf("status=%d location=%q", redirect.Code, redirect.Header().Get("Location"))
|
|
}
|
|
|
|
post := httptest.NewRecorder()
|
|
handler.ServeHTTP(post, httptest.NewRequest(http.MethodPost, OpenAPISpecPath, nil))
|
|
if post.Code != http.StatusMethodNotAllowed || post.Header().Get("Allow") != "GET, HEAD" {
|
|
t.Fatalf("status=%d allow=%q", post.Code, post.Header().Get("Allow"))
|
|
}
|
|
}
|
|
|
|
func TestOpenAPISpecCoversPublicAndManagementEndpoints(t *testing.T) {
|
|
spec := string(openAPISpec)
|
|
for _, want := range []string{
|
|
"openapi: 3.0.3",
|
|
HydrogenQueryPath + ":",
|
|
MileageQueryPath + ":",
|
|
MileageRangeQueryPath + ":",
|
|
StationaryVehicleQueryPath + ":",
|
|
"/api/v2/open-platform/apps:",
|
|
"AppKeyAuth:",
|
|
"AdminBearer:",
|
|
"省略或传空数组时",
|
|
"protocolPriority:",
|
|
"sourceProtocol:",
|
|
StationaryVehicleQueryPath,
|
|
"enum: [GB32960, MQTT, JT808]",
|
|
} {
|
|
if !strings.Contains(spec, want) {
|
|
t.Fatalf("OpenAPI spec missing %q", want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLiveContractDocumentationCoversSerializedFieldsAndQuality(t *testing.T) {
|
|
// Public additions must be discoverable in both the machine contract and the
|
|
// human-readable documentation, including their null/quality companions.
|
|
for _, field := range []string{
|
|
"remainingHydrogenKg", "remainingHydrogenPercent", "hydrogenRecordTime",
|
|
"hydrogenDataStatus", "remainingHydrogenKgStatus", "remainingHydrogenPercentStatus",
|
|
"hydrogenValueSource", "hydrogenSourceProtocol", "hydrogenStaleAfterSeconds",
|
|
"hydrogenExpectedIntervalSeconds", "gpsFixStatus", "locationRecordTime",
|
|
"coordinateSystem", "statisticsStartTime", "statisticsEndTime",
|
|
"updatedAt", "calculationPhase", "qualityStatus", "algorithmVersion",
|
|
"remainingHydrogenPercentSource", "hydrogenFullCapacityKg", "hydrogenTankCapacityL",
|
|
"hydrogenFullPressureMPa", "hydrogenReferenceTemperatureC", "hydrogenEstimatePressureMPa",
|
|
"hydrogenEstimateTemperatureC", "hydrogenPressureTemperatureSource", "hydrogenCalculationVersion",
|
|
"hydrogenCapacitySource", "hydrogenPercentReason",
|
|
} {
|
|
if !strings.Contains(string(openAPISpec), field+":") {
|
|
t.Errorf("OpenAPI missing live contract field %s", field)
|
|
}
|
|
if !strings.Contains(string(simpleDocsHTML), field) {
|
|
t.Errorf("HTML missing live contract field %s", field)
|
|
}
|
|
}
|
|
for name, doc := range map[string]string{"OpenAPI": string(openAPISpec), "HTML": string(simpleDocsHTML)} {
|
|
for _, boundary := range []string{"PARTIAL", "UNSUPPORTED", "UNKNOWN", "PRELIMINARY", "FINAL", "SUSPECT", "REPORTED", "ESTIMATED", "MAX_SENSOR_AGGREGATE", "EXCEEDS_NOMINAL_FULL_CAPACITY", "REAL_GAS_35MPA_15C_V1", "共同快照", "null"} {
|
|
if !strings.Contains(doc, boundary) {
|
|
t.Errorf("%s missing availability/comparability boundary %q", name, boundary)
|
|
}
|
|
}
|
|
}
|
|
if strings.Contains(string(openAPISpec), "MEASURED") || strings.Contains(string(simpleDocsHTML), "MEASURED") {
|
|
t.Error("reported hydrogen must not promise a measured terminal value")
|
|
}
|
|
if strings.Contains(string(simpleDocsHTML), "新接入请使用 sourceProtocol") {
|
|
t.Error("HTML must not advertise sourceProtocol as realtime field")
|
|
}
|
|
}
|