The api does NOT expose a /programs endpoint by design — program names ("FF Uvs", "10min", "SF", etc.) live on the wash bay hardware itself, not in the api.
This test locks that architecture so any future /programs endpoint must be explicitly added and documented, and so the machine-types endpoint remains reachable as the api-side closest equivalent.
Three assertions:
1. No /programs endpoint exists in any route file (or per-module route file)
2. /department/selfserve/machine-types is wired with the list permission and returns the success() envelope
3. /modules/self-serve/lane/relay/machine_program_picker/{status,set,enable} endpoints exist
Refs TRU-19
98 lines
4.4 KiB
PHP
98 lines
4.4 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Program-registry contract tests for TRU-19.
|
|
*
|
|
* Locks the architecture decision that the api does NOT expose a /programs
|
|
* endpoint that returns user-facing program names ("FF Uvs", "10min", "SF",
|
|
* etc.). Those names live on the wash bay hardware itself, not in the api.
|
|
*
|
|
* The api exposes MACHINE TYPES (e.g. "Mafa 5", "Washtec") via
|
|
* /department/selfserve/machine-types and PROGRAM PICKER relay control
|
|
* via /modules/self-serve/lane/relay/machine_program_picker/{status,set,enable,...}.
|
|
*
|
|
* The dashboard (pleno-vue) renders a numeric button registry (0-11) that
|
|
* maps to the physical programs on the wash bay. If a /programs endpoint
|
|
* ever appears in the api by accident, this test will fail and force the
|
|
* author to either (a) document the new endpoint and update this test, or
|
|
* (b) remove the spurious endpoint.
|
|
*
|
|
* Also locks the /department/selfserve/machine-types endpoint as a
|
|
* reachable, list-returning smoke target — this is the closest thing to
|
|
* a /programs endpoint that the api offers, and it should remain stable.
|
|
*/
|
|
|
|
it('does not expose a /programs endpoint (program names live on the wash bay)', function (): void {
|
|
$routesDir = app_path('routes');
|
|
$moduleRoutesDirs = glob(app_path('modules') . '/*/routes') ?: [];
|
|
|
|
$routeFiles = array_merge(
|
|
glob($routesDir . '/*.php') ?: [],
|
|
// Collect per-module route files
|
|
array_merge(...array_map(static fn($dir) => glob($dir . '/*.php') ?: [], $moduleRoutesDirs))
|
|
);
|
|
|
|
expect($routeFiles)->not->toBeEmpty('Expected to find at least one route file');
|
|
|
|
foreach ($routeFiles as $file) {
|
|
$source = file_get_contents($file);
|
|
expect($source)->not->toBeFalse("Failed to read route file: {$file}");
|
|
|
|
// Check for any route that would expose a /programs-style endpoint.
|
|
// The regex matches a $this->get(...) or $this->post(...) call with a
|
|
// /programs URI segment. We use word boundaries to avoid false
|
|
// positives on /modules/self-serve/lane/relay/machine_program_picker/*.
|
|
$matches = preg_match_all(
|
|
'/\$this->(?:get|post|put|delete|patch)\s*\(\s*[\'"]\/[^\'"]*\/programs[\'"]/',
|
|
$source,
|
|
$ignored
|
|
);
|
|
expect($matches)->toBe(
|
|
0,
|
|
"Found a /programs endpoint in {$file}. Program names live on the wash bay hardware — "
|
|
. 'the api should not expose them. If you intentionally want to add one, update this test '
|
|
. 'and document the new endpoint in docs/.'
|
|
);
|
|
}
|
|
});
|
|
|
|
it('exposes /department/selfserve/machine-types as the api-side program-adjacent endpoint', function (): void {
|
|
$machineTypesRoute = file_get_contents(app_path('routes/departmentSelfserveMachineTypesRoute.php'));
|
|
expect($machineTypesRoute)->not->toBeFalse();
|
|
expect($machineTypesRoute)->toContain('/department/selfserve/machine-types');
|
|
expect($machineTypesRoute)->toContain("'list_department_selfserve_machine_types'");
|
|
|
|
// The route must call $response->success(...) which is the standard
|
|
// "200 OK with JSON body" envelope. The contract is: a GET to this
|
|
// endpoint returns a JSON list of machine types.
|
|
expect($machineTypesRoute)->toContain('$response->success(');
|
|
|
|
// The route must enforce the list_* permission so unauthorized callers
|
|
// cannot enumerate machine types.
|
|
expect($machineTypesRoute)->toContain("requirePermission('list_department_selfserve_machine_types')");
|
|
});
|
|
|
|
it('exposes /modules/self-serve/lane/relay/machine_program_picker/* for program picker relay control', function (): void {
|
|
$selfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php'));
|
|
expect($selfServeRoute)->not->toBeFalse();
|
|
|
|
// The program picker relay endpoints must exist. Pest's toContain does
|
|
// not accept a custom failure message, so we collect failures into a
|
|
// single assert at the end with a list of missing endpoints.
|
|
$expectedEndpoints = [
|
|
'/modules/self-serve/lane/relay/machine_program_picker/status',
|
|
'/modules/self-serve/lane/relay/machine_program_picker/set',
|
|
'/modules/self-serve/lane/relay/machine_program_picker/enable',
|
|
];
|
|
|
|
$missing = array_values(array_filter(
|
|
$expectedEndpoints,
|
|
static fn(string $endpoint): bool => !str_contains($selfServeRoute, $endpoint)
|
|
));
|
|
|
|
expect($missing)->toBe(
|
|
[],
|
|
'Missing program picker relay endpoints: ' . implode(', ', $missing)
|
|
);
|
|
});
|