package routers import ( "net/http" "testing" "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform" "github.com/gin-gonic/gin" ) func TestEveryContractHasRegisteredRoute(t *testing.T) { engine := gin.New() RegisterPlatform("heqi", engine) routes := make(map[string]map[string]bool) for _, route := range engine.Routes() { if routes[route.Path] == nil { routes[route.Path] = make(map[string]bool) } routes[route.Path][route.Method] = true } for _, contract := range platform.ExpectedResources() { path := "/heqi/platform/v1" + contract.Path switch contract.Mode { case platform.ReadOnly: assertRouteMethods(t, routes, path, http.MethodGet) assertRouteMethods(t, routes, path+"/:identity", http.MethodGet) assertNoRouteMethods(t, routes, path, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) assertNoRouteMethods(t, routes, path+"/:identity", http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) case platform.AppendOnly: assertRouteMethods(t, routes, path, http.MethodGet, http.MethodPost) assertNoRouteMethods(t, routes, path, http.MethodPut, http.MethodPatch, http.MethodDelete) case platform.Editable: assertRouteMethods(t, routes, path, http.MethodGet, http.MethodPost) assertRouteMethods(t, routes, path+"/:identity", http.MethodGet, http.MethodPut) assertRouteMethods(t, routes, path+"/:identity/status", http.MethodPatch) assertNoRouteMethods(t, routes, path, http.MethodDelete) assertNoRouteMethods(t, routes, path+"/:identity", http.MethodDelete) default: assertRouteMethods(t, routes, path, http.MethodGet, http.MethodPost) assertRouteMethods(t, routes, path+"/:identity", http.MethodGet, http.MethodPut, http.MethodDelete) assertRouteMethods(t, routes, path+"/:identity/status", http.MethodPatch) } } } func TestPlatformGasRouteUsesGasBasic(t *testing.T) { engine := gin.New() RegisterPlatform("heqi", engine) for _, route := range engine.Routes() { if route.Path == "/heqi/platform/v1/gas/gas_station" { t.Fatal("gas station route must use gas_basic as its resource name") } if route.Method == "GET" && route.Path == "/heqi/platform/v1/gas/gas_basic" { return } } t.Fatal("gas_basic list route is not registered") } func TestPlatformOrganizationAndAccountRoutesExposeResourceCRUD(t *testing.T) { engine := gin.New() RegisterPlatform("heqi", engine) routes := make(map[string]map[string]bool) for _, route := range engine.Routes() { if routes[route.Path] == nil { routes[route.Path] = make(map[string]bool) } routes[route.Path][route.Method] = true } for _, resource := range []string{ "/gas/gas_basic", "/gas/gas_account", "/delivery/delivery_basic", "/delivery/delivery_account", "/staff/account", "/staff/credential", "/user/account", "/user/address", "/user/service_relation", "/platform/platfrom_account", "/platform/platform_role", "/platform/platform_menu", } { assertRouteMethods(t, routes, "/heqi/platform/v1"+resource, http.MethodGet, http.MethodPost) assertRouteMethods(t, routes, "/heqi/platform/v1"+resource+"/:identity", http.MethodGet, http.MethodPut, http.MethodDelete) assertRouteMethods(t, routes, "/heqi/platform/v1"+resource+"/:identity/status", http.MethodPatch) } assertRouteMethods(t, routes, "/heqi/platform/v1/platform/platform_role/:identity/menu", http.MethodGet, http.MethodPut) assertRouteMethods(t, routes, "/heqi/platform/v1/platform/platform_role/:identity/menus", http.MethodPut) } func TestPlatformProductCommerceAndDeliveryRoutesFollowTheirContracts(t *testing.T) { engine := gin.New() RegisterPlatform("heqi", engine) routes := make(map[string]map[string]bool) for _, route := range engine.Routes() { if routes[route.Path] == nil { routes[route.Path] = make(map[string]bool) } routes[route.Path][route.Method] = true } for _, resource := range []string{ "/ec/ec_category", "/ec/ec_product", "/ec/ec_product_attribute", "/ec/ec_product_image", "/ec/ec_cart", "/ec/ec_order", "/ec/ec_order_item", "/ec/ec_review", "/delivery/delivery_task", "/delivery/delivery_track", } { assertRouteMethods(t, routes, "/heqi/platform/v1"+resource, http.MethodGet, http.MethodPost) assertRouteMethods(t, routes, "/heqi/platform/v1"+resource+"/:identity", http.MethodGet, http.MethodPut, http.MethodDelete) assertRouteMethods(t, routes, "/heqi/platform/v1"+resource+"/:identity/status", http.MethodPatch) } trackPoint := "/heqi/platform/v1/delivery/delivery_track_point" assertRouteMethods(t, routes, trackPoint, http.MethodGet) assertRouteMethods(t, routes, trackPoint+"/:identity", http.MethodGet) assertNoRouteMethods(t, routes, trackPoint, http.MethodPost) assertNoRouteMethods(t, routes, trackPoint+"/:identity", http.MethodPut, http.MethodDelete) assertNoRouteMethods(t, routes, trackPoint+"/:identity/status", http.MethodPatch) for _, resource := range []string{"/product_type", "/product_warehouse", "/product_info", "/product_repair"} { path := "/heqi/platform/v1" + resource assertRouteMethods(t, routes, path, http.MethodGet, http.MethodPost) assertRouteMethods(t, routes, path+"/:identity", http.MethodGet, http.MethodPut) assertRouteMethods(t, routes, path+"/:identity/status", http.MethodPatch) assertNoRouteMethods(t, routes, path+"/:identity", http.MethodDelete) } owner := "/heqi/platform/v1/product_owner" assertRouteMethods(t, routes, owner, http.MethodGet, http.MethodPost) assertRouteMethods(t, routes, owner+"/:identity", http.MethodGet) assertNoRouteMethods(t, routes, owner+"/:identity", http.MethodPut, http.MethodPatch, http.MethodDelete) for _, resource := range []string{ "/safety/safe_rule", "/safety/safe_event", "/safety/safe_inspection", "/safety/safe_event/:identity/disposals", } { path := "/heqi/platform/v1" + resource assertNoRouteMethods(t, routes, path, http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) assertNoRouteMethods(t, routes, path+"/:identity", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) assertNoRouteMethods(t, routes, path+"/:identity/status", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) } } func TestPlatformFinanceContentRoutesFollowTheirContracts(t *testing.T) { engine := gin.New() RegisterPlatform("heqi", engine) routes := make(map[string]map[string]bool) for _, route := range engine.Routes() { if routes[route.Path] == nil { routes[route.Path] = make(map[string]bool) } routes[route.Path][route.Method] = true } for _, resource := range []string{ "/finance/fin_payment", "/finance/fin_settlement", "/finance/fin_reconciliation", "/content/cms_content", "/customer_service/cs_ticket", } { assertRouteMethods(t, routes, "/heqi/platform/v1"+resource, http.MethodGet, http.MethodPost) assertRouteMethods(t, routes, "/heqi/platform/v1"+resource+"/:identity", http.MethodGet, http.MethodPut, http.MethodDelete) assertRouteMethods(t, routes, "/heqi/platform/v1"+resource+"/:identity/status", http.MethodPatch) } for _, resource := range []string{ "/wallet/wallet", "/wallet/wallet_ledger", "/wallet/wallet_recharge", "/wallet/wallet_withdrawal", } { path := "/heqi/platform/v1" + resource assertRouteMethods(t, routes, path, http.MethodGet) assertRouteMethods(t, routes, path+"/:identity", http.MethodGet) for _, method := range []string{http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete} { if routes[path][method] || routes[path+"/:identity"][method] { t.Errorf("read-only resource %s unexpectedly permits %s", resource, method) } } } for _, resource := range []string{ "/content/cnt_content", "/notification/ntf_template", "/report/report", "/report/report_item", "/report/report_metric_snapshot", "/audit/audit_operation_log", "/audit/audit_export_log", "/audit/audit_approval", } { path := "/heqi/platform/v1" + resource assertNoRouteMethods(t, routes, path, http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) assertNoRouteMethods(t, routes, path+"/:identity", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) } assertNoRouteMethods(t, routes, "/heqi/platform/v1/audit/audit_approval/:identity/approve", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) } func assertRouteMethods(t *testing.T, routes map[string]map[string]bool, path string, methods ...string) { t.Helper() for _, method := range methods { if !routes[path][method] { t.Errorf("route %s %s is not registered", method, path) } } } func assertNoRouteMethods(t *testing.T, routes map[string]map[string]bool, path string, methods ...string) { t.Helper() for _, method := range methods { if routes[path][method] { t.Errorf("route %s %s must not be registered", method, path) } } }