Tests and assessments — technical guide
For maintainers tracing the test catalogue, employee assignments, external launch, result imports and skill updates. Implementation claims below are Verified in code in the local legacy checkout; deployed-code parity is Not verified. See Functional for observed screens and Configuration for administrator guidance.
Scope and architecture
The traced implementation uses Symfony controllers, Twig, jQuery/Ajax, Doctrine, provider API adapters and private report storage in smartmobility-legacy. Paths below are relative to that repository unless explicitly qualified.
This diagram represents the ordinary provider-test path. Internal questionnaires use AssessmentUser and answer records, not the provider attempt model. Level-based English tests add TestCenterLevel and a separate required-score progression flow.
| Component | Responsibility |
|---|---|
src/Company/TestBundle/Controller/InterneController.php | Catalogue filters, detail rendering, scoped participants, unassigned employee picker and reassignment dispatch. |
src/Main/ConfigBundle/Controller/TestController.php | Catalogue search, employee-profile assignment, mapping reads/additions/removals and skill revalidation. |
src/Repository/TestCenterRepository.php | Company-linked catalogue queries and inactive/filter conditions. |
src/Test/MarketPlaceBundle/Controller/SkillValueController.php | Assignment endpoint, mandatory status and provider callback variants. The class name covers multiple providers. |
src/Customer/AccountBundle/Controller/DefaultController.php and its Resources/views/Default/index.html.twig | Employee test centre, availability filtering, pending synchronization and report download. |
src/Customer/TestBundle/Controller/DefaultController.php | Owner-scoped provider launch and internal questionnaire actions. |
src/Service/TestMarketplace/TestMarketplaceService.php | Assignment reuse, lazy provider registration, result retrieval, PDF retrieval, notifications, removal and retake delegation. |
src/Service/TestMarketplace/TestManagerFactory.php | Resolves a tagged manager using the test's type. |
src/Service/TestMarketplace/Managers/ | CT, CG, SV, SVv2, GSB provider behavior. |
src/Company/AccountBundle/Service/ | Company/provider preferences, catalogue links and score-ratio setup. |
src/EventListener/TestListener.php | Return-to-application synchronization using the current_test cookie. |
src/Controller/TestResult/TestResultWebhookController.php | Goshaba callback lookup and result retrieval. |
Data model and lifecycle
| Record | Meaning and important fields |
|---|---|
TestPartner | Provider catalogue identity. |
TestCenter | Test definition: provider/type, metadata, language, external campaign identity and mapping relations. |
SettingTestcenter | Availability link between a company Setting and a test definition. |
TestPartnerClient | Company/provider result-visibility preference. |
TestPartnerKeys | Company/provider credentials; never include stored values in documentation or diagnostic output. |
TestResult | Employee assignment/attempt: user, test, hash, ctid, ctlink, score, global, test_score, completion/invitation/result dates, mandatory flag, visibility and inactivity. |
Testcenterskill, TestCenterLevel, potential mappings | Test-to-skill links, level-test configuration and provider-dependent dimensions. |
SkillLevelScoreRatio | Company conversion bounds for default and Goshaba import paths. |
The TestResult ORM maps score as nullable text. In the report path it is a stored report filename, not the numerical percentage suggested by its name. test_score, provider payloads and skill-level notes are separate data. created/completed are nullable integer timestamps; invitation/result dates are nullable datetimes. No single persisted pending/completed/pass enum was established.
The employee summary counts ordinary company-enabled provider tests as completed when score is nonempty. English-level pass state is computed separately. The completed-card loop checks score and excludes level tests but does not repeat the company's availability-ID filter used by the summary and pending loop. Historic cards can therefore differ from the summary count after availability changes; deactivating a provider does not establish that all old cards disappear.
Observed in application: as Jim Carter in PS Testing, the catalogue displayed 87 tests. After one SQL Senior assignment, Tests opened /plateforme/parametres/, rendered Tests (0/1) with 1 Questionnaires / 0 Complétés / 1 À faire, and showed a mandatory pending card while the flag was enabled. Launch returned to that page with an unavailable-test error and unchanged pending counters. The label Tests réussis does not prove numerical pass/fail behavior for ordinary provider tests.
Assignment and execution flow
- Manager participant loading obtains employee queries from
UserAllowedService, joins results for the chosen test and renders a paginated table. The assignment picker excludes employees already linked to that test. - The assignment endpoint iterates submitted employee/test pairs, calls
getTestResult()and sets mandatory status. Optional assignment mail is sent only for a new record in that endpoint. getTestResult()normally reuses the employee/test record. A new record receives user/test, creation timestamp, source, random hash and the current provider visibility preference. This application lookup is not evidence of a database uniqueness constraint.- The employee launch action loads by both assignment ID and current user.
loadTestUrl()registers the attempt if either provider URL or external ID is missing; otherwise an unreported attempt can trigger synchronization. - Launch redirects back for an unknown/other-user assignment, already reported result or missing provider link. The last case also invokes an error notification. A successful launch dispatches
UserTestsEvent, changes an HTTP link to HTTPS, setscurrent_testuntil tomorrow and redirects externally. - Result retrieval delegates to the selected provider manager and flushes.
TestListenerchecks assignment ownership before synchronizing a cookie-selected result and clears a stale/completed cookie. The employee test-centre controller also callssyncPendingUserTests(). getPdfReport()checks stored content withPdfHelper::isValidPdfContent(), attempts provider import/synchronization when necessary and returns valid content or null. The download action reports PDF unavailability when retrieval fails.
Opening a launch URL can register an external candidate/attempt and dispatch events. Opening the employee test centre can synchronize results. Neither should be treated as a side-effect-free monitoring endpoint.
Reassignment and deletion
reassignUserToTest() rejects reassignment if any matching employee/test record has both null score and null dateResult, then delegates to the provider. BaseTestManager::getTestResult() builds a new attempt carrying test, employee, mandatory status, source and visibility. Provider implementations determine the rest; do not infer universal retake behavior.
Removal checks the current company and mandatory-result conditions, invokes the manager's provider removal operation when present, then removes the local result. A success response does not prove every submitted record was removed because ineligible records can be skipped. No reversible deletion guarantee was established.
HTTP contracts
These are relative application routes, not public integration promises. Route definitions are in config/routes.yaml and the relevant bundle Resources/config/routing.yml. Do not replace local IDs with production IDs when reviewing examples.
| Route and declared method | Input / response behavior |
|---|---|
POST /api/tests/search; XMLHttpRequest required by controller | criterias[search/category/partner/locale/level], offset, limit; returns response, pages, count. Authentication required. |
POST /api/tests/add; XMLHttpRequest condition | Employee-profile assignment: user, add_test[], _token, optional mandatory, sendEmail, tpl. Checks manager context, CSRF token add_test_to_user and employee company. |
GET /api/tests/{id}/skills; XMLHttpRequest required by controller | Company/test availability required; returns mapping IDs, translated skills, potential IDs and DataTables counts. |
POST /api/tests/{id}/skills/add; XMLHttpRequest condition | Nested test mapping rows; optional test[revalidate_skill]. Adds links and can reset skill ratings and assign tests. |
POST /api/tests/{id}/skills/delete; XMLHttpRequest required by controller | Comma-separated test_skills; deletes links and resets matching test-derived skills. |
/plateforme/parametres/; unrestricted in bundle routing | Employee test centre via customer_account_home; opening can synchronize pending results. |
/company/test/{id}; no method restriction in routing | No ID renders catalogue; numeric test ID renders detail or not-found. |
/company/test/action/test_result_users_data; unrestricted | DataTables parameters plus test; returns draw, counts and rendered row cells. Missing company/test produces not-found. |
/company/test/action/company_test_load_users; unrestricted | test_id, limit, offset; returns rendered picker data and total. Missing test produces an empty data response carrying a 404 message value, not necessarily HTTP 404. |
POST /tests/marketplace/skill/email | Form data users[][id], users[][test_id], mandatory, sendEmail; persists assignments and returns positional success tokens plus countUsers. Empty users returns HTTP 404. |
POST /tests/marketplace/test-result/{id}/mandatory | mandatory; use batch with comma-separated ids for batch mode. Returns IDs and boolean status, with 401/403/404/400 checks in the controller. |
/company/test/action/reassignetest; unrestricted | id is a result ID; JSON message success and new id, or error. The spelling is intentional in the current route. |
POST /company/test/result/delete; XMLHttpRequest condition | Comma-separated id; JSON response; unauthenticated 401 and invalid/missing IDs 400. |
/plateforme/tests/pass-test/{id}; unrestricted | Current user's result ID; redirect to provider or employee test centre. This route has write/external side effects. |
For example, the mandatory endpoint returns a shape such as the following after a successful authorized update. IDs are illustrative; this example was not sent to the application.
{"ids":[12345],"mandatory":true}
The assignment endpoint is named skill/email but can persist assignments without sending mail. Its submitted form values use PHP boolean coercion; callers must not assume the literal string "false" is equivalent to a false boolean.
Provider callbacks include SkillValue, SkillValue v2 and CodinGame routes in the marketplace bundle. The Goshaba action declares POST /goshaba under its configured controller mount; its complete deployed callback URL was not verified and is deliberately omitted. It requires campaignId and externalId, resolves provider+campaign then test+external attempt ID, and triggers a result load. Empty/missing fields yield 204, unresolved campaign/attempt 404, and successful lookup/retrieval yields 200. This is not an authenticated webhook contract audit.
Observed assignment and launch verification
The documentation sample used CodinGame test Administrateur base de données (SQL) - Senior in PS Testing, with Jim Carter as both the active account and the sole selected employee. The assignment dialog had mandatory and assignment-email options unchecked. Submission displayed Vous avez correctement ajouté 1 collaborateur à ce test. The participant table then showed Jim, the current date, Non Passé and an empty completion date.
Changing the participant Obligatoire switch returned the untranslated success token view_test.set_mandatory_success; the checked state survived a reload. The employee card then showed Ce test est obligatoire. The flag was returned to optional after verification. No assessment answers or skill ratings were submitted.
The employee card opened the owner-scoped launch route in a new tab, which returned to the test centre with Ce test ne semble pas disponible. Notre équipe support va contacter l'éditeur et reviendra vers vous. This establishes the visible missing-launch-link outcome, not its precise root cause or successful delivery of an operational notification. The existing local record remained pending; no completed report was obtained.
Provider-specific result and skill handling
The factory constructs the manager class name from TestCenter.type; missing types/managers return null. The orchestration then skips unsupported manager operations rather than guaranteeing a provider result.
SVv2TestManager::setScoreToTestSkills()resolves the test's skill mappings, computes a note and callsSkillImportServicewithsource=Testand the provider percentage when the employee has a job. Its level-test path is separate.CGTestManager::supports()requires the company feature valuescompany_api_CGand nonemptycompany_api_CG_url.assignUserToTest()sends campaign ID, candidate email/name and manager email toCodingGameApi::assignUserToCampaign(), then records returnedidandtest_urlwhen present. The API adapter sends bothsend_invitation_email=falseandsend_notification_email_on_bounce=false. Missing configuration, rejected API output or missing response fields can leave the required identifiers/link absent. No live credential values or provider response were inspected.GSBTestManagerhas its own dimension preparation and score conversion. Goshaba potentials and catalogue credentials are separate from the default provider configuration.CTTestManagerretrieves provider results/reports and has separate potential and score calculation logic.- Legacy profile templates identify source
Test, show test metadata when available and suppress the ordinary mastery self-rating control for those skills.
Do not generalize one adapter's thresholds, timestamp format, retry behavior or skill-write conditions to all providers.
Catalogue search and mapping changes
TestCenterRepository::findFilteredTests() and countFilteredTests() require a SettingTestcenter link for the current company and apply t.isInactive != 1. Name uses a contains search; provider, category, language and level use equality filters when supplied. Results are ordered by name with offset/limit pagination. The front-end option showOnlyPublished does not establish a separate publication-state predicate: the inspected repository uses inactive status. SQL nullable values can also affect != 1 inclusion; no live null-status case was exercised.
Main/ConfigBundle/Controller/TestController::addTestSkillsAction() verifies company context and the company/test link, restricts selected skills to that company, deduplicates matching test/skill/potential/company links and persists new links. When a new link is inserted:
- If
test[revalidate_skill]is present, it loads matching company test-derived skills throughfindUserTestCenterSkills(), sets note to0, source to null and test score to null, then flushes. Presence is checked, so a supplied false-like value is not equivalent to omitting the key. - It finds employees whose current job includes the skills via
findUserSkillJob()and callsassignTest(..., false, 'REASSIGN'). This call does not send assignment mail; downstream reassignment is provider-specific. - It returns added/ignored counts and mapping data. The
skills_resetresponse references a different variable from the collection used in the reset, andtest_result_resetis not proof of a provider reset. Inspect resulting records rather than treating those counters as authoritative.
The template controls the skills tab with edit_test_skill and offers company_test_reinittest revalidation as a checkbox checked by default. The add handler does not repeat these exact role checks, so UI visibility and server authorization must be evaluated separately.
deleteTestSkillsAction() scopes mapping deletion to the company and test. Its subsequent portfolio reset queries by selected skill objects and source Test, without an explicit user-company or originating-test predicate. findUserSkillJob() likewise has no explicit tenant parameter; both depend on the selected skill identities. Reuse of skill objects or multiple tests for the same skill requires careful scope review. Removing a link can clear ratings attributed to tests beyond the removed link; no complete provenance safeguard was established. Mapping mutations were not exercised. The populated detail exposed Match de compétences and the checked revalidation option; local assignment and mandatory status were exercised separately.
Configuration and change propagation
HandleTestPartnerSavingService requires a Setting and nonempty payload. It extracts test_{providerId} keys and corresponding visibility-type_{providerId} values, writes preferences, creates company/test links in batches of 50, then cleans up excluded preferences/links. Unknown selected providers return 404; missing context/payload returns 400.
Existing-result visibility updates target company and provider and default to is_changed_visibility=0; explicitly changed records are excluded. A missing visibility selection is not established as a safe default, and PHP/DB null handling must be checked before programmatic use.
The provider settings GET action calls checkSkillLevelScoreRatio(), which persists missing ratio rows. Saving Goshaba keys immediately invokes a catalogue import. Saving score ratios invokes recalculation across company skills with source Test. The recalculation's inspected upper-bound comparisons use array-valued operands; correct scalar boundary behavior is Not verified. This requires execution against approved sample data before relying on bulk recalculation.
The callback controller contains a FEATURE_SWITCH check for test_skill_change in one SkillValue result path. This is not evidence of a global switch governing every provider's skill updates. Check the exact adapter and call site.
Permissions and security boundaries
Owner scoping is explicit in the employee launch query. Observed in application: Jim can view the manager catalogue, but the provider-settings route displays You are not authorized to access this resource. Verified in code: company settings require a top-level company School; the controller returns before ratio initialization for rejected accounts. The inspected firewall restricts /company to ROLE_COMPANY, /plateforme to ROLE_CUSTOMER and /api to ROLE_USER, with HTTPS requirements; these broad route rules do not replace record-level checks. Mandatory updates check company membership and the mandatory_tests role where a permission object exists. Participant/picker population scoping uses UserAllowedService; UI controls additionally use test_reassigned and test_d.
These observations do not establish equivalent authorization in every mutation action. Assignment, reassignment, report downloads and sharing need route/firewall/voter checks reviewed alongside controller code before exposing them as integrations. No complete CSRF, callback-authentication, IDOR or report-access audit was performed. The employee-profile /api/tests/add path explicitly checks its CSRF token and employee tenant; do not assume the separate skill/email assignment path has identical checks. Catalogue visibility is not a security boundary by itself.
Provider registration transmits candidate information according to each adapter. Reports are saved using the tenant's private media directory, with a result/time-based filename. Keep personal results, report links, candidate identifiers, credentials and provider URLs out of documentation screenshots and routine logs. The captured catalogue screenshot contains no employee results.
Failure modes and observability
| Symptom | Trace |
|---|---|
| Empty catalogue | Company/test availability links, provider catalogue import, filters and isInactive predicate. The latest inspection displayed 87 tests. A populated catalogue does not establish working provider launch. |
| Assignment exists but no launch URL | Factory/type resolution, adapter support, provider candidate/attempt creation, configured credentials and missing-link notification. |
| Result not imported | Cookie ownership/expiry, test-centre pending synchronization, adapter retrieval and provider callback lookup. No universal synchronization SLA was established. |
| Invalid/missing PDF | Private media lookup, provider report import, valid-PDF check and PDF-unavailable flash. |
| Retake rejected | Another pending result or provider-specific reassignment outcome. |
| Incorrect converted level | Provider mapper, mapping records, ratio type, boundary handling and current employee context. |
Use the application's protected operational logs and provider diagnostics with company and attempt correlation. The documentation inspection did not establish a dedicated metrics dashboard, durable retry queue, callback idempotency guarantee or deployment job schedule. Command classes under src/Command/TestMarketplace/ and src/Command/ImportTestCenter/ are available integration entry points; their presence does not prove they run automatically.
Runtime dependencies, coexistence and extension points
This feature depends on the legacy Symfony application, Doctrine database, Twig/JavaScript assets, configured provider APIs, mail delivery and private media storage. Optional catalogue/result commands depend on their deployed scheduling and credentials. No mandatory AIHub call was identified in the traced orchestration; a targeted search of AIHub skill/configuration sources found no matching provider-test implementation.
Targeted nextgen front-end/back-end searches found no TestMarketplace/TestCenter/provider implementation equivalent to this catalogue. The live catalogue and Jim's personal Tests page use legacy routes in a current navigation shell. Do not infer a data migration or replacement merely from redesigned navigation.
To extend support, trace TestManagerInterface, factory registration, type codes, catalogue import, assignment, synchronization, report retrieval, retake/removal, score mapping and company visibility together. A provider name added to a template does not establish working end-to-end support. Validate tenant isolation and the provider's result lifecycle before enabling a new adapter.
Evidence and limitations
- Verified in code: the controller/service paths listed above;
src/Main/ConfigBundle/Resources/config/doctrine/{TestResult,TestCenter,TestCenterLevel}.orm.yml;src/Main/ConfigBundle/Repository/Test/TestResultRepository.php; provider managers, factory, callback routing and company settings services;src/Main/ConfigBundle/Controller/TestController.php, itsResources/config/routing/test.yml,src/Repository/TestCenterRepository.php,src/Main/ConfigBundle/Repository/UserskillRepository.phpandconfig/packages/security.yaml. - Observed in application: Chrome, 23 October 2026, PS Testing 2026 as Jim Carter: manager catalogue with 87 Tests, populated test detail, saved sample assignment to Jim, mandatory persistence, mapping panel/dialog, employee pending card with 0/1, unavailable-test launch outcome, and denied company provider configuration. The functional/configuration guides contain full-window PS Testing captures; no capture from another tenant remains referenced.
- Inferred: failure-diagnosis order and extension guidance are maintainer recommendations.
- Not verified: deployed revision parity, provider execution, populated reports, callback authentication, cross-provider idempotency, full endpoint authorization, queue/scheduler behavior and exact score-boundary results. One sample assignment and its mandatory status were saved; owner-scoped launch was exercised and failed to produce a usable provider test. Mandatory status was restored to optional. No mapping writes, answers, result sharing, removals or provider-setting changes were executed. Successful provider execution, callback/result retrieval and operational-notification delivery remain unverified.
Functional-guide evidence
These implementation and product references support the functional guide. Local source behavior does not establish which revision or optional features are deployed. Application observations remain distinct from code and documentary evidence.
- Observed in application: Chrome, 23 October 2026, PS Testing 2026 as Jim Carter only for this update: manager catalogue, Performance → Tests Marketplace, personal Tests, populated catalogue, sample assignment submission/confirmation, pending participant row, mandatory-status persistence, mapping dialog, employee pending card/counters, provider launch error and denied provider settings. The catalogue screenshot and the configuration guide's denial capture come from this PS Testing session. The personal screen is described without embedding its contact details.
- Verified in code:
smartmobility-legacy/src/Company/TestBundle/Controller/InterneController.php, itsResources/views/Interne/templates,src/Customer/AccountBundle/{Controller/DefaultController.php,Resources/views/Default/index.html.twig},src/Customer/TestBundle/Controller/DefaultController.php, andsrc/Service/TestMarketplace/. - Inferred: the diagnostic sequence above is recommended support guidance, not a guarantee of the cause of a particular error.
- Not verified: assignment email delivery (disabled in the sample), successful external test execution, completed reports, provider subscriptions and full role coverage. One sample assignment was created for Jim; mandatory status was tested and returned to optional. No answers, skill mappings, result sharing or company provider settings were saved. All application work remained in PS Testing as Jim Carter.
Journey verification — 26 October 2026
Observed in application: PS Testing 2026, Jim Carter. Opening the existing assigned SQL Senior test returned an unavailable-test message to the test centre. No provider attempt, result callback or conversion could be established. No support communication was sent; the support-contact sentence is application text.
Last updated on 3 November 2026