doc/code-walkthrough.md
The Shields codebase is divided into several parts:
*.js in the root of services*.js in the folders of servicesThe tests are also divided into several parts:
badge-maker/**/*.spec.jscore/**/*.spec.jsservices/*.spec.jsservices/*/**/*.spec.jscypress/e2e/*.cy.js*.tester.js in subfolders of servicesOur goal is to reach 100% coverage of the code in the frontend, core, and service helper functions when the unit and functional tests are run.
Our test strategy for the service code is a bit different. It’s primarily based on live integration tests. That’s because service response formats can change, and when they do the badges break. We want our tests to fail when this happens. That way we can fix the problems proactively instead of waiting for users to report them. There’s a good discussion about this decision in #927. It’s acceptable to write mocked tests of logic that is difficult to reach using live tests, however where possible, it’s preferred to test this kind of logic through unit tests (e.g. of render() and transform() functions).
The server entrypoint is server.js which sets up error reporting, loads config, and creates an instance of the server.
The Server, which is defined in core/server/server.js, is based on the web framework Scoutcamp. It creates an http server, sets up helpers for token persistence and monitoring. Then it loads all the services, injecting dependencies as it asks each one to register its route with Scoutcamp.
The service registration continues in BaseService.register. From its route property, it derives a regular expression to match the route path, and invokes camp.route with this value.
At this point the situation gets gnarly and hard to follow. For the purpose of initialization, suffice it to say that camp.route invokes a callback with the four parameters ( queryParams, match, end, ask ) which is created in a legacy helper function in legacy-request-handler.js. This callback delegates to a callback in BaseService.register with three different parameters ( queryParams, match, sendBadge ), which then runs BaseService.invoke. BaseService.invoke instantiates the service and runs BaseService#handle.
( queryParams, match, end, ask ). This callback is defined in legacy-request-handler. A timeout is set to handle unresponsive service code and the next callback is invoked: the legacy handler function.( queryParams, match, sendBadge ). Its job is to extract data from the regex match and queryParams, and then invoke sendBadge with the result.BaseService.register. It works by running BaseService.invoke, which instantiates the service, injects more dependencies, and invokes BaseService.handle which is implemented by the service subclass.handle(), which should be implemented by each service subclass, is to return an object which partially describes a badge or throw one of the handled error classes. "Partially rendered" most commonly means a non-empty message and an optional color. In the case of the Endpoint badge, it could include many other parameters. At the time of writing the handled error classes were NotFound, InvalidResponse, Inaccessible, and InvalidParameter. Throwing any other error is a programmer error which will be reported and described to the user as a shields internal error.handle() function delegates to one or more helpers to handle stages of the request:
{ isError, message, color }.coalesceBadge whose job is to coalesce query string overrides with values from the service and the service’s defaults to produce an object that fully describes the badge to be rendered.sendBadge is invoked with that object. It does some housekeeping on the timeout. Then it renders the badge to svg or raster and pushes out the result over the HTTPS connection.