140 lines
10 KiB
Markdown
140 lines
10 KiB
Markdown
|
|
# Implementation Plan
|
||
|
|
|
||
|
|
- [x] 1. Write bug condition exploration test
|
||
|
|
- **Property 1: Bug Condition** — Jira API Compliance Violations
|
||
|
|
- **CRITICAL**: This test MUST FAIL on unfixed code — failure confirms the bugs exist
|
||
|
|
- **DO NOT attempt to fix the test or the code when it fails**
|
||
|
|
- **NOTE**: This test encodes the expected behavior — it will validate the fix when it passes after implementation
|
||
|
|
- **GOAL**: Surface counterexamples that demonstrate the three compliance violations
|
||
|
|
- **Scoped PBT Approach**: Scope properties to the three concrete bug conditions:
|
||
|
|
1. `searchIssues()` sends POST instead of GET — generate random JQL strings and assert the HTTP method captured is `GET` and the request path starts with `/rest/api/2/search?` with query parameters (not a JSON body)
|
||
|
|
2. `getIssue()` sends a single-issue GET to `/rest/api/2/issue/{key}` — generate random issue keys and assert the request path contains `/rest/api/2/search` (not `/rest/api/2/issue/`)
|
||
|
|
3. `searchIssuesByKeys()` builds JQL without `project =` — generate random arrays of issue keys and assert the JQL string passed to the search contains `project =`
|
||
|
|
- Mock `jiraRequest` to capture HTTP method, URL path, and body arguments without making real HTTP calls
|
||
|
|
- Use `fast-check` arbitraries to generate JQL strings, issue keys (e.g., `fc.tuple(fc.stringMatching(/^[A-Z]{2,6}$/), fc.integer({ min: 1, max: 99999 }))` for `KEY-123` patterns), and key arrays
|
||
|
|
- Test file: `backend/__tests__/jira-api-compliance.property.test.js`
|
||
|
|
- Run test on UNFIXED code
|
||
|
|
- **EXPECTED OUTCOME**: Test FAILS (this is correct — it proves the bugs exist)
|
||
|
|
- Document counterexamples found: `searchIssues()` uses POST, `getIssue()` hits `/rest/api/2/issue/{key}`, `searchIssuesByKeys()` JQL lacks `project =`
|
||
|
|
- Mark task complete when test is written, run, and failure is documented
|
||
|
|
- _Requirements: 1.1, 1.2, 1.3_
|
||
|
|
|
||
|
|
- [x] 2. Write preservation property tests (BEFORE implementing fix)
|
||
|
|
- **Property 2: Preservation** — Unchanged Jira API Functions
|
||
|
|
- **IMPORTANT**: Follow observation-first methodology
|
||
|
|
- Observe behavior on UNFIXED code for non-buggy functions:
|
||
|
|
- `createIssue({ project: { key: 'TEST' }, summary: 'x', issuetype: { name: 'Task' } })` sends `POST` to `/rest/api/2/issue` with JSON body containing `{ fields: {...} }`
|
||
|
|
- `updateIssue('TEST-1', { summary: 'y' })` sends `PUT` to `/rest/api/2/issue/TEST-1` with JSON body containing `{ fields: {...} }`
|
||
|
|
- `addComment('TEST-1', 'comment text')` sends `POST` to `/rest/api/2/issue/TEST-1/comment` with JSON body containing `{ body: 'comment text' }`
|
||
|
|
- `transitionIssue('TEST-1', '5')` sends `POST` to `/rest/api/2/issue/TEST-1/transitions` with JSON body containing `{ transition: { id: '5' } }`
|
||
|
|
- `getTransitions('TEST-1')` sends `GET` to `/rest/api/2/issue/TEST-1/transitions`
|
||
|
|
- `testConnection()` sends `GET` to `/rest/api/2/myself`
|
||
|
|
- Write property-based tests using `fast-check` that verify for all generated inputs:
|
||
|
|
1. `createIssue()` always sends `POST /rest/api/2/issue` with `{ fields }` body — generate random field objects
|
||
|
|
2. `updateIssue()` always sends `PUT /rest/api/2/issue/{key}` with `{ fields }` body — generate random keys and field objects
|
||
|
|
3. `addComment()` always sends `POST /rest/api/2/issue/{key}/comment` with `{ body }` — generate random keys and comment strings
|
||
|
|
4. `transitionIssue()` always sends `POST /rest/api/2/issue/{key}/transitions` with `{ transition: { id } }` — generate random keys and transition IDs
|
||
|
|
5. `getTransitions()` always sends `GET /rest/api/2/issue/{key}/transitions` — generate random keys
|
||
|
|
6. `testConnection()` always sends `GET /rest/api/2/myself`
|
||
|
|
7. Response shape: `searchIssues()` returns `{ ok, data: { total, issues } }` and `getIssue()` returns `{ ok, data: <issue> }` — verify shape is preserved
|
||
|
|
- Mock `jiraRequest` to capture method, path, body and return appropriate mock responses
|
||
|
|
- Test file: `backend/__tests__/jira-api-preservation.property.test.js`
|
||
|
|
- Verify tests pass on UNFIXED code
|
||
|
|
- **EXPECTED OUTCOME**: Tests PASS (this confirms baseline behavior to preserve)
|
||
|
|
- Mark task complete when tests are written, run, and passing on unfixed code
|
||
|
|
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10_
|
||
|
|
|
||
|
|
- [x] 3. Fix the core API helper (`backend/helpers/jiraApi.js`)
|
||
|
|
|
||
|
|
- [x] 3.1 Convert `searchIssues()` from POST to GET with query parameters
|
||
|
|
- Replace `jiraPost('/rest/api/2/search', body)` with `jiraGet('/rest/api/2/search?jql=...&fields=...&maxResults=...&startAt=...')`
|
||
|
|
- URL-encode JQL string with `encodeURIComponent(jql)`
|
||
|
|
- Comma-join and encode fields array with `encodeURIComponent(fields.join(','))`
|
||
|
|
- Encode `maxResults` and `startAt` as query parameters
|
||
|
|
- Remove the JSON body object `{ jql, startAt, maxResults, fields }`
|
||
|
|
- Preserve the `res.status === 200` check and `JSON.parse(res.body)` response parsing
|
||
|
|
- Preserve the `{ ok, data }` return shape
|
||
|
|
- _Bug_Condition: searchIssues uses POST /rest/api/2/search with JSON body_
|
||
|
|
- _Expected_Behavior: searchIssues uses GET /rest/api/2/search?jql=&fields=&maxResults=&startAt=_
|
||
|
|
- _Preservation: Response shape { ok, data: { total, issues } } unchanged_
|
||
|
|
- _Requirements: 2.1_
|
||
|
|
|
||
|
|
- [x] 3.2 Add project scoping to `searchIssuesByKeys()` JQL
|
||
|
|
- Change JQL from `` key in (${keyList}) AND updated >= -24h `` to `` key in (${keyList}) AND updated >= -24h AND project = ${JIRA_PROJECT_KEY} ``
|
||
|
|
- `JIRA_PROJECT_KEY` is already available in module scope
|
||
|
|
- _Bug_Condition: searchIssuesByKeys JQL lacks project = clause_
|
||
|
|
- _Expected_Behavior: JQL includes project = JIRA_PROJECT_KEY_
|
||
|
|
- _Preservation: Return shape and searchIssues delegation unchanged_
|
||
|
|
- _Requirements: 2.2_
|
||
|
|
|
||
|
|
- [x] 3.3 Refactor `getIssue()` to delegate to `searchIssues()` via JQL
|
||
|
|
- Replace `jiraGet('/rest/api/2/issue/${encodeURIComponent(issueKey)}?fields=...')` with a call to `searchIssues()` using JQL `key = "${issueKey}" AND project = ${JIRA_PROJECT_KEY}` and `maxResults: 1`
|
||
|
|
- Extract `data.issues[0]` from search results to return as `{ ok: true, data: <issue> }`
|
||
|
|
- Return `{ ok: false, status: 404, body: 'Issue not found' }` when search returns empty results
|
||
|
|
- Preserve the `{ ok, data: <single-issue> }` return shape for callers
|
||
|
|
- _Bug_Condition: getIssue sends GET /rest/api/2/issue/{key} (single-issue GET)_
|
||
|
|
- _Expected_Behavior: getIssue delegates to searchIssues with JQL key = "{key}" AND project = KEY_
|
||
|
|
- _Preservation: Return shape { ok, data: { key, fields } } unchanged_
|
||
|
|
- _Requirements: 2.3_
|
||
|
|
|
||
|
|
- [x] 3.4 Verify bug condition exploration test now passes
|
||
|
|
- **Property 1: Expected Behavior** — Jira API Compliance Violations
|
||
|
|
- **IMPORTANT**: Re-run the SAME test from task 1 — do NOT write a new test
|
||
|
|
- The test from task 1 encodes the expected behavior
|
||
|
|
- When this test passes, it confirms the expected behavior is satisfied
|
||
|
|
- Run `npx jest backend/__tests__/jira-api-compliance.property.test.js --no-cache`
|
||
|
|
- **EXPECTED OUTCOME**: Test PASSES (confirms bugs are fixed)
|
||
|
|
- _Requirements: 2.1, 2.2, 2.3_
|
||
|
|
|
||
|
|
- [x] 3.5 Verify preservation tests still pass
|
||
|
|
- **Property 2: Preservation** — Unchanged Jira API Functions
|
||
|
|
- **IMPORTANT**: Re-run the SAME tests from task 2 — do NOT write new tests
|
||
|
|
- Run `npx jest backend/__tests__/jira-api-preservation.property.test.js --no-cache`
|
||
|
|
- **EXPECTED OUTCOME**: Tests PASS (confirms no regressions)
|
||
|
|
- Confirm all unchanged functions still produce the same HTTP method, path, and body
|
||
|
|
|
||
|
|
- [x] 4. Update the UAT test script (`backend/scripts/jira-uat-test.js`)
|
||
|
|
|
||
|
|
- [x] 4.1 Update test case 3 name to reflect JQL-based pattern
|
||
|
|
- Change `'3. Get Single Issue (GET /issue/{key})'` to `'3. Get Single Issue (JQL search)'`
|
||
|
|
- The test body calls `jiraApi.getIssue()` which now delegates to JQL search — no logic change needed in the test function itself
|
||
|
|
- _Requirements: 2.4_
|
||
|
|
|
||
|
|
- [x] 4.2 Update test case 8 name to reflect GET method
|
||
|
|
- Change `'8. JQL Search (POST /search)'` to `'8. JQL Search (GET /search)'`
|
||
|
|
- Add project-scoped JQL to the test: include `AND project = ${jiraApi.JIRA_PROJECT_KEY}` in the JQL string passed to `searchIssues()`
|
||
|
|
- _Requirements: 2.5_
|
||
|
|
|
||
|
|
- [x] 4.3 Update test case 9 to verify project scoping
|
||
|
|
- Add a log entry or assertion that the bulk key search includes project scoping
|
||
|
|
- The underlying `searchIssuesByKeys()` now includes `project = <KEY>` — the test validates the function works correctly with the compliant JQL
|
||
|
|
- _Requirements: 2.5_
|
||
|
|
|
||
|
|
- [x] 5. Update the API documentation (`docs/jira-api-use-cases.md`)
|
||
|
|
|
||
|
|
- [x] 5.1 Update compliance summary table
|
||
|
|
- Change "Bulk reads via JQL" row endpoint from `POST /rest/api/2/search` to `GET /rest/api/2/search`
|
||
|
|
- Add a row for "Single-issue fetch" describing JQL-based lookup via `GET /rest/api/2/search?jql=key="KEY"&...`
|
||
|
|
- _Requirements: 2.6, 2.7_
|
||
|
|
|
||
|
|
- [x] 5.2 Update Use Case 3 (Get Single Issue)
|
||
|
|
- Change endpoint from `GET /rest/api/2/issue/{issueKey}?fields=...` to `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=<KEY>&fields=...&maxResults=1`
|
||
|
|
- Update the description to explain the JQL-based pattern
|
||
|
|
- _Requirements: 2.7_
|
||
|
|
|
||
|
|
- [x] 5.3 Update Use Case 8 (JQL Search / Bulk Sync)
|
||
|
|
- Change endpoint from `POST /rest/api/2/search` to `GET /rest/api/2/search?jql=...&fields=...&maxResults=...&startAt=...`
|
||
|
|
- Update JQL pattern to include `project = <KEY>` scoping
|
||
|
|
- _Requirements: 2.6_
|
||
|
|
|
||
|
|
- [x] 5.4 Update Use Case 9 (Issue Lookup)
|
||
|
|
- Change endpoint from `GET /rest/api/2/issue/{issueKey}?fields=...` to `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=<KEY>&fields=...&maxResults=1`
|
||
|
|
- Update the description to match the JQL-based lookup pattern
|
||
|
|
- _Requirements: 2.7_
|
||
|
|
|
||
|
|
- [x] 6. Checkpoint — Ensure all tests pass
|
||
|
|
- Run `npx jest backend/__tests__/jira-api-compliance.property.test.js --no-cache` — all bug condition tests pass
|
||
|
|
- Run `npx jest backend/__tests__/jira-api-preservation.property.test.js --no-cache` — all preservation tests pass
|
||
|
|
- Run `npx jest --no-cache` — all existing tests in the project still pass
|
||
|
|
- Ensure all tests pass, ask the user if questions arise.
|