RedlineKnowledge base

Canonical fixture data

Every test in this plan asserts against one mock GitLab repository. The mock server (T0.1), the fixture site (T0.2), the unit tests and the Playwright specs all use the values on this page. Do not invent other values; if a test needs something not listed here, add it here first in the same commit.

Project

FieldValue
Project id1
Project pathgroup/project
Default branchmain
Document pathsrc/content/doc.md
Site id (service)demo
Content roots (service)["src/content"]

The mock’s public and internal GitLab URL are the mock’s own origin. In the Playwright harness that is http://localhost:9999.

Users

idnameusernameemailavatar_urlweb_url
2Test Usertest[email protected]/avatar.png/test

GET /api/v4/[email protected] returns [user 2]. GET /api/v4/user with Authorization: Bearer mock-user-token returns user 2 including email.

Commits on main (newest first)

Nameshashorttitlecommitted_dateparent_idssignature
c3"c".repeat(40)ccccccccAdd section2026-07-30T12:00:00Z[c2]PGP, verified
c2"b".repeat(40)bbbbbbbbRevise intro2026-07-29T12:00:00Z[c1]404 (unsigned)
c1"a".repeat(40)aaaaaaaaAdd doc2026-07-28T12:00:00Z[]404 (unsigned)

All commits: author_name: "Test User", author_email: "[email protected]", web_url: "<public>/group/project/-/commit/<sha>", stats: {additions, deletions, total} may be any integers.

GET /api/v4/projects/1/repository/commits?path=src/content/doc.md&ref_name=main returns [c3, c2, c1]. With ref_name=revise it returns [d1, c3, c2, c1].

Tags: v1.0.0 at c3. GET .../repository/tags returns [{ name: "v1.0.0", commit: { id: c3 } }].

Document content by ref

Exact text, each file ends with a single newline. Refs resolve for both the sha and the branch name.

V1 at c1:

# Title

Old paragraph.

V2 at c2:

# Title

Revised paragraph.

V3 at c3 and at main:

# Title

Revised paragraph.

## Details

Details paragraph.

VMR at d1 and at revise:

# Title

Proposed paragraph.

## Details

Details paragraph.

GET /api/v4/projects/1/repository/files/src%2Fcontent%2Fdoc.md?ref=<ref> returns { content: base64(text), blob_id: "<ref>", commit_id: <sha of ref>, last_commit_id: <sha of ref> }. A ref the table does not list returns 404.

The fixture site file test/fixtures/site/src/content/doc.md must equal V3 byte for byte. A unit test enforces this (T0.2).

Block ids

Derived by parseDocumentBlocks in semantic.js (package root after T2.1). Listed here so tests can assert them; never hand-edit the algorithm to match this table.

VersionBlock ids in order
V1document:heading:title, title:paragraph:paragraph
V2same ids as V1; paragraph content differs
V3document:heading:title, title:paragraph:paragraph, title:heading:details, title/details:paragraph:paragraph
VMRsame ids as V3; paragraph content differs

Expected semantic operations:

  • diffDocumentBlocks(V1, V2): one replace of title:paragraph:paragraph, confidence high.
  • diffDocumentBlocks(V2, V3): two inserts: title:heading:details after title:paragraph:paragraph, then title/details:paragraph:paragraph after title:heading:details.
  • diffDocumentBlocks("", V1): two inserts: document:heading:title (afterBlockId null), title:paragraph:paragraph.
  • diffDocumentBlocks(V3, VMR): one replace of title:paragraph:paragraph, before Revised paragraph., after Proposed paragraph..

Expected blockHistory for the document at main (T2.3), newest first:

{
  "document:heading:title": [{ "sha": "aaaa…", "type": "insert" }],
  "title:paragraph:paragraph": [{ "sha": "bbbb…", "type": "replace" }, { "sha": "aaaa…", "type": "insert" }],
  "title:heading:details": [{ "sha": "cccc…", "type": "insert" }],
  "title/details:paragraph:paragraph": [{ "sha": "cccc…", "type": "insert" }]
}

Blocks changed since a revision (T3.6): since c1 = 3, since c2 = 2, since c3 = 0.

Expected diffWords("Revised paragraph.", "Proposed paragraph.") (T2.6):

[
  { "type": "delete", "text": "Revised" },
  { "type": "insert", "text": "Proposed" },
  { "type": "equal", "text": " paragraph." }
]

Merge request !7

FieldValue
iid7
titleRevise wording
stateopened, draft: false
source_branch / target_branchrevise / main
sha (head)d1 = "d".repeat(40)
source_project_id1
detailed_merge_statusmergeable, has_conflicts: false
diff_refs{ base_sha: c3, head_sha: d1, start_sha: c3 }
authoruser 2
web_url<public>/group/project/-/merge_requests/7

Commit d1: title Propose wording, committed 2026-07-31T12:00:00Z, parent [c3], unsigned.

  • GET .../merge_requests?state=opened&target_branch=main returns [!7].
  • GET .../merge_requests/7 returns !7.
  • GET .../merge_requests/7/diffs returns [{ old_path: "src/content/doc.md", new_path: "src/content/doc.md", diff: "@@ -3 +3 @@\n-Revised paragraph.\n+Proposed paragraph.\n" }].
  • GET .../merge_requests/7/approvals returns { approved_by: [], approvals_left: 0 }.
  • GET .../merge_requests/7/discussions returns the discussions created on it (initially []).
  • GET .../merge_requests/7/commits returns [d1].

Issues and discussions (stateful)

Initially no issues. The service creates the document issue on the first published comment with title [Document Review] src/content/doc.md.

  • GET .../issues?state=opened&search=...&in=title returns matching open issues.
  • POST .../issues creates { iid: <next from 1>, id: <same>, title, description, state: "opened", web_url: "<public>/group/project/-/issues/<iid>" }.
  • GET .../issues/:iid/discussions returns that issue’s discussions.
  • POST .../issues/:iid/discussions with { body } creates a discussion { id: "<32 hex>", individual_note: false, notes: [note] } where the note is { id: <next from 1>, body, author: user 2, created_at, updated_at, system: false, resolvable: false }. The author is always user 2 (the mock has one user).
  • GET .../issues/:iid/discussions/:id returns one discussion.
  • POST .../issues/:iid/discussions/:id/notes with { body } appends a note.
  • PUT .../issues/:iid/notes/:noteId with { body } updates the note body.
  • DELETE .../issues/:iid/notes/:noteId removes the note; removing the first note removes the discussion.

Merge-request discussions follow the same shapes under .../merge_requests/7/... and support PUT .../merge_requests/7/discussions/:id with { resolved }.

Write endpoints require Authorization: Bearer mock-user-token; any other authorization returns 401. Read endpoints accept PRIVATE-TOKEN: mock-read-token or the bearer token.

OAuth

  • GET /oauth/authorize?client_id=…&redirect_uri=R&state=S&… responds 302 Location: R?code=mock-code&state=S. No page, no consent.
  • POST /oauth/token with code=mock-code responds { access_token: "mock-user-token", token_type: "Bearer", expires_in: 7200, scope: "api" }. Any other code responds 400.
  • POST /oauth/revoke responds 200 {}.

Service reachability

  • GET /api/v4/version responds { version: "18.3.0", revision: "mock" } with no authorization required.

Control endpoints

Not part of GitLab. Used by tests only.

EndpointEffect
POST /__mock/resetdiscard all created issues, discussions, notes; set down=false
POST /__mock/downevery /api/* and /oauth/* request responds 503 { message: "down" } until /__mock/up
POST /__mock/upclear down
GET /__mock/state{ down: boolean, issues: [...], discussions: { "<kind>/<iid>": [...] } }
Git history

Loading the page's history…