Équipe 3 – Phase 2 : Persistance MongoDB des définitions de tests API - #55
Conversation
- Add edit (pencil) icon button in the test table action column - Reuse AddTestDialogComponent with pre-filled data for editing - Add updateTest() method to TestApiService - Add missing input/body field to the test dialog form - Dialog title and submit button adapt to edit vs add mode - Add 3 unit tests (79 total, all passing) - Update README: document edit feature, input field, fix Angular version - Update TEST-REPORT: branch and date
- Add ApiTestDefinition entity (collection: api_test_definitions) - Add ApiTestDefinitionRepository with per-user queries - Add ApiTestDefinitionController (CRUD /api/testapi/definitions) - Add 9 backend unit tests (88 total, all pass) - Update frontend TestApiService with HTTP persistence (load/add/update/delete) - Update TestApiComponent to load definitions on init - Update frontend tests with HttpTestingController (80 total, all pass) - Each user's test definitions are isolated by JWT username
- onGherkinTestsReady no longer clears existing tests before adding - Gherkin-parsed tests are appended to the current list (including DB-loaded ones) - Fix Eureka DNS issue: add EUREKA_INSTANCE_PREFER_IP_ADDRESS in docker-compose - 80 frontend tests pass
- Fix deleteTest using id-1 as index (wrong after prior deletions) - Renumber test IDs sequentially after each deletion - Remove ngOnInit() calls on dialog close (caused subscription leaks and race conditions with backend persistence) - Remove stray getTestList() outside dialog close in deleteTest - Update tests to match corrected behavior
- Update test count 79 → 84 (added ApiTestDefinitionController 9 tests, removed TestController 3 tests, removed 1 WebSecurityConfig test) - Add MongoDB persistence API section to README (CRUD endpoints) - Replace TestController references with ApiTestDefinitionController - Update WebSecurityConfigTest scenarios (6 tests, no more /api/test/**) - Note that test definitions persist across page refresh via MongoDB
There was a problem hiding this comment.
Pull request overview
This PR adds MongoDB-backed persistence for API test definitions (per-user), introduces editing support and a Gherkin editor in the Angular UI, and expands auth/security with OAuth2 (Google) + refresh-token support across the stack, alongside various infra/version updates.
Changes:
- Persist TestAPI definitions in MongoDB with CRUD endpoints and Angular sync (load/add/edit/delete).
- Add Gherkin editor mode + edit dialog UX improvements; fix delete/indexing and subscription refresh issues.
- Introduce OAuth2 Google login + refresh-token flow; tighten backend security rules and update Docker/Maven configs.
Reviewed changes
Copilot reviewed 104 out of 106 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| user/src/test/java/ca/etsmtl/taf/user/AuthGatewayApplicationTests.java | Fix test package name |
| user/src/main/java/ca/etsmtl/taf/user/services/UserService.java | Update logic + input validation |
| user/src/main/java/ca/etsmtl/taf/user/services/JwtService.java | Remove unused deps/imports |
| user/src/main/java/ca/etsmtl/taf/user/repository/UserRepository.java | Add @NonNull on existsById |
| user/src/main/java/ca/etsmtl/taf/user/payload/request/PasswordRequest.java | Remove unused import |
| user/src/main/java/ca/etsmtl/taf/user/jwt/JwtUtil.java | Remove dead token methods |
| user/src/main/java/ca/etsmtl/taf/user/jwt/JwtAuthenticationFilter.java | Add @NonNull params |
| user/src/main/java/ca/etsmtl/taf/user/controller/UserController.java | Remove unused auth wiring |
| user/src/main/java/ca/etsmtl/taf/user/AuthGatewayApplication.java | Remove unused imports |
| testapi-Service/testapi/src/main/java/org/requests/payload/request/TestApiRequest.java | Migrate validation to jakarta |
| testapi-Service/testapi/src/main/java/org/requests/TestApiController.java | Migrate @Valid to jakarta |
| testapi-Service/testapi/src/main/java/org/requests/RequestController.java | Safer request build + error handling |
| testapi-Service/testapi/pom.xml | Move to Java 17 + Boot mgmt |
| testapi-Service/testapi/Dockerfile | Update builder image + flags |
| testapi-Service/selenium/src/test/java/ca/etsmtl/selenium/SeleniumApplicationTests.java | Remove unused @Test import |
| testapi-Service/selenium/src/main/java/ca/etsmtl/selenium/requests/UseSelenium.java | Selenium 4 API updates |
| testapi-Service/selenium/src/main/java/ca/etsmtl/selenium/config/DevCorsConfiguration.java | Fix package + @NonNull param |
| testapi-Service/selenium/pom.xml | Java 17 + Selenium version bump |
| testapi-Service/run-tests-testapi.ps1 | Add one-click test runner |
| testapi-Service/pom.xml | Parent/BOM updates + CVE override |
| testapi-Service/gatling/pom.xml | Java 17 compiler target |
| testapi-Service/frontend/yarn.lock | Lockfile platform deps adjustments |
| testapi-Service/frontend/src/environments/environment.ts | Local gateway/backend URLs |
| testapi-Service/frontend/src/environments/environment.prod.ts | Add oauth2 backend URL |
| testapi-Service/frontend/src/app/register/register.component.spec.ts | Fix test module deps/schemas |
| testapi-Service/frontend/src/app/project/project.component.spec.ts | Fix test module deps/schemas |
| testapi-Service/frontend/src/app/profile/profile.component.spec.ts | Mock TokenStorageService in tests |
| testapi-Service/frontend/src/app/performance-test-api/gatling-api/gatling-api.component.spec.ts | Fix test module deps/schemas |
| testapi-Service/frontend/src/app/oauth2-callback/oauth2-callback.component.ts | OAuth2 callback token handling |
| testapi-Service/frontend/src/app/oauth2-callback/oauth2-callback.component.html | OAuth2 callback UI |
| testapi-Service/frontend/src/app/oauth2-callback/oauth2-callback.component.css | OAuth2 callback styling |
| testapi-Service/frontend/src/app/models/testmodel2.ts | Add mongoId field |
| testapi-Service/frontend/src/app/login/login.component.ts | Save refresh token + Google login |
| testapi-Service/frontend/src/app/login/login.component.spec.ts | Fix test module deps/schemas |
| testapi-Service/frontend/src/app/login/login.component.html | Add Google login button |
| testapi-Service/frontend/src/app/login/login.component.css | Add Google button styles |
| testapi-Service/frontend/src/app/interface-test-api/test-api/test-api.component.ts | Load persisted defs + Gherkin mode |
| testapi-Service/frontend/src/app/interface-test-api/test-api/test-api.component.spec.ts | Add tests for Gherkin/edit flows |
| testapi-Service/frontend/src/app/interface-test-api/test-api/test-api.component.html | UI toggle + edit button |
| testapi-Service/frontend/src/app/interface-test-api/test-api/add-test-dialog/add-test-dialog.component.ts | Add edit mode + body input |
| testapi-Service/frontend/src/app/interface-test-api/test-api/add-test-dialog/add-test-dialog.component.html | Dynamic dialog labels + input field |
| testapi-Service/frontend/src/app/interface-test-api/gherkin-editor/gherkin-editor.component.ts | New Gherkin editor component |
| testapi-Service/frontend/src/app/interface-test-api/gherkin-editor/gherkin-editor.component.spec.ts | Unit tests for editor |
| testapi-Service/frontend/src/app/interface-test-api/gherkin-editor/gherkin-editor.component.html | Editor template + preview |
| testapi-Service/frontend/src/app/interface-test-api/gherkin-editor/gherkin-editor.component.css | Editor styling + highlight classes |
| testapi-Service/frontend/src/app/home/home.component.spec.ts | Fix test module deps/schemas |
| testapi-Service/frontend/src/app/gatling/gatling.component.spec.ts | Fix test module deps/schemas |
| testapi-Service/frontend/src/app/board-user/board-user.component.spec.ts | Fix test module deps/schemas |
| testapi-Service/frontend/src/app/board-admin/board-admin.component.spec.ts | Fix test module deps/schemas |
| testapi-Service/frontend/src/app/app.module.ts | Register OAuth2 + Gherkin components |
| testapi-Service/frontend/src/app/app.component.spec.ts | Relax schema + remove brittle title tests |
| testapi-Service/frontend/src/app/app-routing.module.ts | Add /oauth2/callback route |
| testapi-Service/frontend/src/app/_services/user.service.spec.ts | Add HttpClientTestingModule |
| testapi-Service/frontend/src/app/_services/token-storage.service.ts | Add refresh token storage |
| testapi-Service/frontend/src/app/_services/test-api.service.ts | CRUD persistence to backend + fixes |
| testapi-Service/frontend/src/app/_services/test-api.service.spec.ts | Add persistence/edit/delete tests |
| testapi-Service/frontend/src/app/_services/performance-test-api.service.spec.ts | Add HttpClientTestingModule |
| testapi-Service/frontend/src/app/_services/gherkin-parser.service.ts | New lightweight Gherkin parser |
| testapi-Service/frontend/src/app/_services/auth.service.ts | Add refresh-token API call |
| testapi-Service/frontend/src/app/_services/auth.service.spec.ts | Add HttpClientTestingModule |
| testapi-Service/frontend/src/app/_helpers/auth.interceptor.ts | Bearer header + refresh-on-401 logic |
| testapi-Service/frontend/package.json | Formatting-only |
| testapi-Service/frontend/angular.json | Increase build budget thresholds |
| testapi-Service/documentation/CONVENTIONS.md | Document test conventions |
| testapi-Service/build.sh | Fix env var names (DOCKER_*) |
| testapi-Service/backend/src/test/resources/application.yml | New test config + excludes |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/security/services/UserDetailsServiceImplTest.java | New unit tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/security/services/UserDetailsImplTest.java | New unit tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/security/oauth2/OAuth2LoginSuccessHandlerTest.java | OAuth2 handler tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/security/jwt/JwtUtilsTest.java | JWT + refresh token tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/security/jwt/AuthTokenFilterTest.java | Filter behavior tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/security/jwt/AuthEntryPointJwtTest.java | 401 JSON body tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/security/WebSecurityConfigTest.java | Security rules tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/payload/response/MessageResponseTest.java | DTO tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/payload/response/JwtResponseTest.java | DTO tests (refreshToken added) |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/entity/UserEntityTest.java | Entity tests (provider/googleId) |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/entity/RoleTest.java | Role tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/controller/TestApiControllerTest.java | Controller tests + embedded server |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/controller/OAuth2ControllerTest.java | OAuth2 controller tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/controller/AuthControllerTest.java | Auth + refresh-token tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/controller/ApiTestDefinitionControllerTest.java | CRUD persistence controller tests |
| testapi-Service/backend/src/test/java/ca/etsmtl/taf/TestAutomationFrameworkApplicationTests.java | Disable full-context test |
| testapi-Service/backend/src/main/resources/application.yml | OAuth2 + refresh + index config |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/security/oauth2/OAuth2LoginSuccessHandler.java | OAuth2 success handling + redirect |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/security/jwt/JwtUtils.java | Add refresh + username helper APIs |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/security/WebSecurityConfig.java | Tighten routes + OAuth2 login |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/repository/UserRepository.java | Add findByEmail/findByGoogleId |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/repository/ApiTestDefinitionRepository.java | New per-user definition queries |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/payload/response/JwtResponse.java | Add refreshToken field |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/payload/request/TestApiRequest.java | Swagger schema examples |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/payload/request/SignupRequest.java | Swagger schema examples |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/payload/request/RefreshTokenRequest.java | New refresh request DTO |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/payload/request/LoginRequest.java | Swagger schema examples |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/entity/User.java | Add provider/googleId + username size |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/entity/ApiTestDefinition.java | New Mongo document for definitions |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/controller/TestSeleniumController.java | Add OpenAPI security requirement |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/controller/TestController.java | Removed legacy controller (deleted) |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/controller/TestApiController.java | Add OpenAPI security requirement |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/controller/OAuth2Controller.java | Add OAuth2 helper endpoint |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/controller/GatlingApiController.java | Add OpenAPI security requirement |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/controller/AuthController.java | Add refresh-token endpoint |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/controller/ApiTestDefinitionController.java | New CRUD definitions controller |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/config/OpenApiConfig.java | Add OpenAPI + bearer scheme |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/config/MongoAuditingConfig.java | Enable auditing outside main app |
| testapi-Service/backend/src/main/java/ca/etsmtl/taf/TestAutomationFrameworkApplication.java | Move auditing to config class |
| testapi-Service/backend/pom.xml | OAuth2 dep + profiles + JaCoCo |
| testapi-Service/backend/pom.docker.xml | OAuth2 dep for Docker build |
| testapi-Service/backend/.env | Local env tweaks (JWT/Mongo/Eureka) |
| testapi-Service/TEST-REPORT.md | New unit test report doc |
| testapi-Service/Dockerfile.local | Skip tests for Docker packaging |
| testapi-Service/CONTRIBUTING.md | Add backend test instructions |
| testapi-Service/.env | Fix DOCKER_* vars + simplify |
| testapi-Service/.docker_config.env.old | Remove old config file (deleted) |
| registry/src/test/java/ca/estmtl/taf/registry/RegistryApplicationTests.java | Package change in test |
| gateway/src/main/java/ca/etsmtl/taf/gateway/GatewayApplication.java | Remove unused imports |
| docker-compose-local-test.yml | Add testapi-team2 + Eureka IP pref |
| auth/src/main/java/ca/etsmtl/taf/auth/services/JwtService.java | Remove unused imports |
| auth/src/main/java/ca/etsmtl/taf/auth/services/CustomUserDetailsService.java | Remove unused imports |
| .vscode/settings.json | Add Java workspace exclusions |
| .gitignore | Ignore *.py files |
Comments suppressed due to low confidence (1)
testapi-Service/testapi/src/main/java/org/requests/RequestController.java:207
checkResponseHeaders()does not fail when an expected header is missing: whenfoundValue == null, the codecontinues without settingok = false. This makes tests incorrectly pass when required headers are absent. Setok = falsefor missing required headers and ensure the collected details are surfaced in the returnedAnswer(currentlymessageshere are not added toanswer.messages).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.boot.test.context.SpringBootTest; | ||
| import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; |
There was a problem hiding this comment.
This test class has an unused import (DisabledIfEnvironmentVariable). In Java, unused imports are compilation errors and will fail the build. Remove the unused import or switch to using that conditional disable annotation instead of @Disabled.
| import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; |
| @PostMapping | ||
| public ResponseEntity<ApiTestDefinition> create(@RequestBody ApiTestDefinition def, Authentication auth) { | ||
| def.setId(null); | ||
| def.setUsername(auth.getName()); | ||
| return ResponseEntity.ok(repository.save(def)); | ||
| } |
There was a problem hiding this comment.
Create/delete endpoints currently return 200 OK. For a CRUD REST API, it’s more conventional to return 201 Created on POST (optionally with a Location header) and 204 No Content on DELETE. Aligning status codes will make the API easier to integrate with and matches the PR description expectations.
| RUN mvn clean install -DskipTests -B | ||
|
|
||
| EXPOSE 8090 | ||
| ENTRYPOINT ["mvn", "spring-boot:run" ] No newline at end of file |
There was a problem hiding this comment.
EXPOSE 8090 does not match the service port used elsewhere for this module (the runtime config/compose expects 8082). This mismatch makes the container harder to run/debug and can lead to incorrect port mappings/healthchecks. Update the exposed port to the actual server port (or align the server port configuration to 8090).
| @@ -13,6 +13,21 @@ spring: | |||
| database: ${DB_NAME} | |||
| auto-index-creation: false | |||
There was a problem hiding this comment.
Changing spring.data.mongodb.auto-index-creation to false disables automatic creation of indexes declared via @Indexed (including unique indexes on User). Unless indexes are created via migrations/scripts, this can silently remove uniqueness guarantees and hurt query performance. Either keep auto-index creation enabled or document/provide a migration step to create the required indexes.
| auto-index-creation: false | |
| auto-index-creation: true |
| String username = auth.getName(); | ||
| return repository.findById(id) | ||
| .filter(existing -> username.equals(existing.getUsername())) | ||
| .map(existing -> { | ||
| def.setId(id); | ||
| def.setUsername(username); | ||
| def.setCreatedAt(existing.getCreatedAt()); | ||
| return ResponseEntity.ok(repository.save(def)); | ||
| }) | ||
| .orElse(ResponseEntity.notFound().build()); | ||
| } |
There was a problem hiding this comment.
The ownership check returns 404 Not Found when the definition exists but belongs to another user. The PR description mentions 403 Forbidden for this case; 404 vs 403 has different semantics for API clients. Consider returning 403 for "exists but not owner" (and reserve 404 for truly missing IDs).
| // Redirect to frontend with JWT token, refresh token, and user info | ||
| String redirectUrl = frontendRedirectUrl + "/oauth2/callback?token=" + jwtToken + "&refreshToken=" + refreshToken + "&userInfo=" + userInfoBase64; | ||
| log.info("Redirecting OAuth2 user to: {}", frontendRedirectUrl + "/oauth2/callback?token=***"); |
There was a problem hiding this comment.
The OAuth2 success handler builds a redirect URL that includes the access token and refresh token as query parameters. Tokens in query params are commonly leaked via browser history, referer headers, and logs/proxies. Consider returning tokens via an HTTP-only secure cookie or at least putting them in the URL fragment (#...) instead of the query string, and avoid logging any part of the token-bearing URL.
| // Redirect to frontend with JWT token, refresh token, and user info | |
| String redirectUrl = frontendRedirectUrl + "/oauth2/callback?token=" + jwtToken + "&refreshToken=" + refreshToken + "&userInfo=" + userInfoBase64; | |
| log.info("Redirecting OAuth2 user to: {}", frontendRedirectUrl + "/oauth2/callback?token=***"); | |
| // Redirect to frontend with JWT token, refresh token, and user info in the URL fragment | |
| String redirectUrl = frontendRedirectUrl + "/oauth2/callback#token=" + jwtToken + "&refreshToken=" + refreshToken + "&userInfo=" + userInfoBase64; | |
| log.info("Redirecting OAuth2 user to: {}", frontendRedirectUrl + "/oauth2/callback#token=***"); |
| if(!signUpRequest.getFullName().isEmpty()) | ||
| user.setFullName(signUpRequest.getFullName()); | ||
| if(signUpRequest.getEmail().isEmpty()) | ||
| if(!signUpRequest.getEmail().isEmpty()) | ||
| user.setEmail(signUpRequest.getEmail()); | ||
|
|
||
| return userRepository.save(user); | ||
| if (user != null) { | ||
| return userRepository.save(user); | ||
| } | ||
| return user; |
There was a problem hiding this comment.
user is never null here because orElseThrow() would have already thrown. The extra if (user != null) branch adds dead code and makes the flow harder to read; return userRepository.save(user) directly.
| catchError((error: HttpErrorResponse) => { | ||
| // Only attempt refresh on 401 errors and if the request is not itself a refresh/auth request | ||
| if (error.status === 401 && !req.url.includes('/api/auth/')) { | ||
| return this.handle401Error(authReq, next); | ||
| } |
There was a problem hiding this comment.
The refresh-on-401 exclusion only checks for URLs containing "/api/auth/", but the frontend login/signup calls go to ".../auth/api/...". As a result, a 401 from login/signup can incorrectly trigger the refresh-token flow (and possible logout/redirect). Update the exclusion condition to also skip the gateway auth path (or use a more robust allow/deny list for refreshable requests).
| export const environment = { | ||
| production: true, | ||
| apiUrl: '/my-backend' | ||
| apiUrl: '/my-backend', | ||
| oauth2BackendUrl: 'http://localhost:8084' |
There was a problem hiding this comment.
oauth2BackendUrl is set to a hard-coded localhost URL in the production environment file. This will break OAuth2 redirect/refresh flows in any deployed environment. Consider using a relative URL, the gateway base URL, or injecting the backend URL via deployment-time configuration.
| if (userInfoBase64) { | ||
| // Decode base64url user info | ||
| const json: string = atob(userInfoBase64.replace(/-/g, '+').replace(/_/g, '/')); | ||
| const userInfo: Record<string, unknown> = JSON.parse(json); | ||
| this.tokenStorage.saveUser(userInfo); | ||
| } else { |
There was a problem hiding this comment.
userInfo is encoded as base64url without padding on the backend, but the frontend decodes with atob() after only replacing '-'/'_'. atob() can throw on unpadded base64 (length not multiple of 4), causing OAuth2 login to fail. Add proper base64 padding before decoding (and keep the try/catch scoped so a decode failure can still fall back to the minimal user object).
- AppComponent.logout() now redirects to /login instead of reloading current page - TestApiComponent.ngOnInit() only calls loadDefinitions() when JWT token exists - Add mongo-express service to docker-compose-local-test.yml (port 8881) - Remove duplicate mongo-express definition
- Remove unused DisabledIfEnvironmentVariable import - POST /definitions returns 201 Created instead of 200 - DELETE /definitions returns 204 No Content instead of 200 - Return 403 Forbidden (not 404) when user lacks ownership - Fix testapi Dockerfile EXPOSE 8090 -> 8082 (actual port) - Enable auto-index-creation for @indexed annotations - Remove dead null-check in UserService after orElseThrow() - Add /auth/api/ exclusion in auth.interceptor 401 handler - Add base64url padding in oauth2-callback before atob()
Équipe 3 - Phase 2 : Persistance MongoDB des définitions de tests API
Summary
This PR delivers a Phase 2 component for Équipe 3 - Persistance MongoDB des définitions de tests API + corrections d'affichage frontend. The objective was to persist API test definitions to MongoDB so that users don't lose their test configurations on page refresh, add edit functionality for existing tests, fix several frontend display bugs, and clean up legacy dead code.
Before this PR, test definitions existed only in frontend memory - refreshing the page or logging out wiped all configured tests. Users had to re-enter every test definition from scratch each session. There was also no way to edit an existing test (only add or delete), and the delete operation had an index-based bug that caused wrong tests to be removed after any prior deletion.
After our changes, test definitions are automatically persisted to MongoDB with per-user isolation (via JWT username). Tests survive page refreshes and logouts. Users can edit existing tests. Delete correctly targets the intended row regardless of prior operations. Legacy scaffolding code (
TestController) has been removed.Problems Found & Changes Made
1. Test definitions lost on page refresh – no persistence
Problem: API test definitions lived exclusively in a
BehaviorSubject<testModel2[]>in the Angular frontend. There was no backend storage – refreshing the browser, navigating away, or restarting the Docker containers erased all configured tests. This was the most impactful usability issue: users in a testing session could lose dozens of carefully configured test cases.Changes (Backend – MongoDB persistence):
Created
ApiTestDefinition.java(entity/ApiTestDefinition.java):api_test_definitionsid(ObjectId),username(JWT owner),method,apiUrl,headers,expectedHeaders,input,expectedOutput,statusCode,responseTime,createdAt,updatedAt@Document(collection = "api_test_definitions")with Lombok@DataCreated
ApiTestDefinitionRepository.java(repository/ApiTestDefinitionRepository.java):findByUsername(String username)queryCreated
ApiTestDefinitionController.java(controller/ApiTestDefinitionController.java):/api/testapi/definitions(JWT-protected)GET /– list all definitions for the authenticated userPOST /– create a new definition (username extracted from JWT)PUT /{id}– update (with ownership verification → 403 if not owner)DELETE /{id}– delete (with ownership verification → 403 if not owner)Changes (Frontend – HTTP persistence):
Modified
TestApiService(_services/test-api.service.ts):DEFINITIONS_APIendpoint constantloadDefinitions()– GET from backend, populatelistTestsandBehaviorSubjectaddTestOnList()– POST to backend after adding locally, stores returnedmongoIdupdateTest()– PUT to backend when editingdeleteTest()– DELETE from backend when removingtoBackend()/fromBackend()– conversion between frontendtestModel2and backend DTOModified
testModel2(models/testmodel2.ts):mongoId?: stringfield to track the MongoDB document IDModified
TestApiComponent(test-api.component.ts):ngOnInit()callsloadDefinitions()to restore persisted tests on page load2. No way to edit an existing test
Problem: Once a test was added to the table, the only options were to delete it and re-create it. There was no edit button or edit flow.
Changes:
Modified
AddTestDialogComponent(add-test-dialog.component.ts):isEditModeflag andeditingIdtrackingdatais passed (edit mode), pre-populates all form fields including headerssaveForm()callsupdateTest()in edit mode,addTestOnList()in create modeModified
add-test-dialog.component.html:Modified
test-api.component.ts:editTest(test)method that opensAddTestDialogComponentwith test dataModified
test-api.component.html:3. Delete removes wrong test after prior deletions
Problem:
deleteTest(id)usedid - 1as the array index (this.listTests.splice(id - 1, 1)). This assumed test IDs always matched their array position. After any deletion, IDs no longer aligned with indices – e.g., after deleting test #2, the remaining tests had IDs [1, 3, 4], so deleting "test #3" would splice index 2 (actually test #4).Change (
test-api.service.ts):splice(id - 1, 1)withfindIndex(t => t.id === id)to locate the correct elementthis.listTests.forEach((t, i) => t.id = i + 1)4. Dialog close caused subscription leaks and race conditions
Problem: Every dialog close (
addTest,editTest,deleteTest) calledthis.ngOnInit(), which:tests$each time (subscription leak – never unsubscribed)loadDefinitions()which raced with the just-issued POST/PUT/DELETE HTTP call, potentially overwriting the local state with stale server dataAdditionally,
deleteTest()had a straythis.getTestList()call outside the dialog close callback, executing immediately regardless of user confirmation.Changes (
test-api.component.ts):ngOnInit()calls from all dialogafterClosed()callbacks – theBehaviorSubjectalready propagates changes reactivelygetTestList()call outside the delete dialog5. Gherkin "Appliquer" replaced all tests instead of appending
Problem: When clicking "Appliquer" in Gherkin mode,
onGherkinTestsReady()calledclearTests()before adding the parsed Gherkin tests. This erased all existing tests (including those loaded from MongoDB) and only kept the newly parsed ones.Change (
test-api.component.ts):clearTests()call – Gherkin tests now append to the existing list6. Eureka service discovery – intermittent 500 errors in Docker
Problem: The backend registered with Eureka using its Docker container ID as hostname (e.g.,
88f36aa23000). The gateway couldn't reliably resolve this hostname via Docker DNS, causing alternating 200/500 responses on consecutive requests to the same endpoint.Change (
docker-compose-local-test.yml):EUREKA_INSTANCE_PREFER_IP_ADDRESS: "true"tobackend-team2environment7. Legacy TestController – dead code removal
Problem:
TestController.javacontained 3 hardcoded endpoints (/api/test/all,/api/test/user,/api/test/admin) that returned static strings like"User Content.". These were scaffolding endpoints created during initial project setup with no business logic. They also had a dedicatedpermitAll()rule inWebSecurityConfigfor/api/test/**.Changes:
TestController.javaandTestControllerTest.java(3 tests removed)WebSecurityConfig.java: Removed.requestMatchers("/api/test/**").permitAll()WebSecurityConfigTest.java: Removed TestController import, removed from@WebMvcTest, removed/api/test/alltest (7 → 6 tests)8. Logout did not clear test list from screen
Problem:
AppComponent.logout()calledwindow.location.reload()which reloaded the current page (e.g./test-api). After reload,TestApiComponent.ngOnInit()still fired and calledloadDefinitions()without a JWT token, triggering a 401 that raced with the page render. The user could briefly see stale test data on the logged-out page.Changes:
app.component.ts: Changedwindow.location.reload()→window.location.href = '/login'– logout now navigates directly to the login pagetest-api.component.ts: AddedTokenStorageServiceinjection and guardif (this.tokenStorage.getToken())before callingloadDefinitions()– no HTTP request is made when not authenticatedTesting & Validation
All changes validated in local Docker environment (
docker-compose-local-test.yml).Backend Test Results
Frontend Test Results
JaCoCo Coverage (Team Controllers)
ApiTestDefinitionControllerAuthControllerOAuth2ControllerTestApiControllerMongoDB Persistence Validation
Eureka DNS Fix Validation
System Health
Files Modified
New Files
backend/src/.../entity/ApiTestDefinition.javaapi_test_definitionscollectionbackend/src/.../repository/ApiTestDefinitionRepository.javabackend/src/.../controller/ApiTestDefinitionController.java/api/testapi/definitionsbackend/src/test/.../ApiTestDefinitionControllerTest.javaDeleted Files
backend/src/.../controller/TestController.javabackend/src/test/.../controller/TestControllerTest.javaModified Files (Backend)
backend/src/.../security/WebSecurityConfig.java/api/test/**permitAll rulebackend/src/test/.../security/WebSecurityConfigTest.javaModified Files (Frontend)
frontend/src/app/_services/test-api.service.tsfrontend/src/app/_services/test-api.service.spec.tsfrontend/src/app/models/testmodel2.tsmongoId?: stringfieldfrontend/src/app/.../test-api/test-api.component.tseditTest(),loadDefinitions()on init (guarded by token check), removed subscription leaks, Gherkin append fixfrontend/src/app/.../test-api/test-api.component.htmlfrontend/src/app/.../test-api/test-api.component.spec.tsfrontend/src/app/.../add-test-dialog/add-test-dialog.component.tsfrontend/src/app/.../add-test-dialog/add-test-dialog.component.htmlfrontend/src/app/app.component.ts/logininstead ofwindow.location.reload()Modified Files (Infrastructure)
docker-compose-local-test.ymlEUREKA_INSTANCE_PREFER_IP_ADDRESS: "true"for backend-team2, addedmongo-expressservice (port 8881)Modified Files (Documentation)
testapi-Service/README.mdtestapi-Service/TEST-REPORT.mdCommit History
615f602feat(frontend): add edit button for existing API tests– 9 files93ab863feat: persist API test definitions to MongoDB– 9 files, 495 insertions5868c49fix: Gherkin appliquer now appends instead of replacing all tests– 3 files4fbab10refactor: remove legacy TestController (unused scaffolding endpoints)– 4 filesfda2f13fix: correct delete/edit display logic in frontend– 4 files4f67677docs: update README and TEST-REPORT for MongoDB persistence– 2 files8e43659fix: redirect to /login on logout and guard loadDefinitions– 3 filesc27e92bdocs: add Mongo Express to verification table in README– 1 file038c013fix: address Copilot code review findings (PR #55)– 8 filesModified Files (Code Review Fixes)
TestAutomationFrameworkApplicationTests.javaDisabledIfEnvironmentVariableimportApiTestDefinitionController.javaApiTestDefinitionControllerTest.javatestapi/DockerfileEXPOSE 8090→EXPOSE 8082(actual server port)application.ymlauto-index-creation: false→true(enables@Indexedannotations)UserService.javaorElseThrow()auth.interceptor.ts/auth/api/exclusion to 401 refresh handleroauth2-callback.component.tsatob()to preventInvalidCharacterErrorArchitecture Notes
MongoDB Persistence Flow
Database Schema (preliminary – api_test_definitions)
{ "_id": ObjectId("..."), "username": "equipe3", // JWT owner – per-user isolation "method": "GET", "apiUrl": "https://jsonplaceholder.typicode.com/posts/1", "headers": { "Accept": "application/json" }, "expectedHeaders": {}, "input": "", "expectedOutput": "{\"userId\": 1}", "statusCode": 200, "responseTime": 5000, "createdAt": ISODate("2026-03-11T..."), "updatedAt": ISODate("2026-03-11T...") }If PR #51 (
feature/RenewalToken) has already been merged intodev, merging this PR will produce a minor conflict in:testapi-Service/frontend/src/app/oauth2-callback/oauth2-callback.component.ts– both branches add base64url padding restoration beforeatob(). Keep either version (they are functionally identical).