Skip to content

feat(parity): padDiff port, compactPad, anonymizeAuthor, preAuthorize hooks#280

Merged
SamTV12345 merged 1 commit into
mainfrom
feat/parity-round3
Jun 11, 2026
Merged

feat(parity): padDiff port, compactPad, anonymizeAuthor, preAuthorize hooks#280
SamTV12345 merged 1 commit into
mainfrom
feat/parity-round3

Conversation

@SamTV12345

Copy link
Copy Markdown
Member

Summary

Third parity round vs the original etherpad-lite — four areas implemented in parallel:

createDiffHTML (padDiff port)

  • New lib/paddiff package: faithful port of padDiff.ts — clear-authorship detection, deletion changesets that re-insert removed text carrying author + removed attributes, changeset composition, revision-range validation.
  • GET /admin/api/pads/{padId}/diffHTML?startRev=N[&endRev=M] renders via the existing HTML export and returns {html, authors}.

compactPad (original API 1.3.1)

  • POST /admin/api/pads/{padId}/compact ({keepRevisions: N}) reusing the admin UI's revision compaction (AdminMessageHandler.DeleteRevisions).

anonymizeAuthor (GDPR, original API 1.3.1)

  • Manager.AnonymizeAuthor mirrors the original's ordering (sever token binding first, zero name/color, null chat authorship; pad content stays intact; idempotent).
  • POST /admin/api/author/{authorId}/anonymize.
  • New DataStore primitives RemoveTokenOfAuthor + ClearChatAuthorship on all four backends; GetChatsOfPad LEFT JOINs so anonymized messages stay retrievable.

preAuthorize / preAuthzFailure plugin hooks

  • Ported from the original webaccess Step 1: explicit permit skips authentication (never on /admin-auth), deny → 403 unless a preAuthzFailure hook overrides the response, no answer defers to the regular flow. Wired into the server middleware.

Bug fixed along the way

  • adminutils.CreateRevision mutated a by-value pool copy that shared the caller's maps, corrupting the pad pool — Pad.Check() then failed with numToAttrib length does not match nextNum and the admin revision cleanup 500ed (reproducible: create a pad without an author, run Check() twice). Fixed with a real deep copy (new APool.Clone); the dead, nil-map-writing APool.clone was removed.

Companion change (separate repo)

The client side of suggestUserName (adopting a suggested name + sending USERINFO_UPDATE) was implemented in etherpad-webcomponents on branch feat/client-message.

Test plan

  • go test -p 1 ./... — all 28 packages green (Memory/SQLite/Postgres/MySQL), go vet ./... clean
  • New tests: 6 paddiff unit tests, compact/diffHTML API tests (24 subtests across backends), anonymize manager + API tests (idempotency, 404, chat scrubbing), 8 preAuthorize/preAuthzFailure middleware tests

🤖 Generated with Claude Code

… hooks

Next parity round vs the original etherpad, implemented in parallel:

createDiffHTML + compactPad:
- new lib/paddiff package: full port of padDiff.ts (clear-authorship
  detection, deletion changesets re-inserting removed text with
  author + removed attributes, composition, valid revision ranges)
- GET /admin/api/pads/{padId}/diffHTML?startRev=N&endRev=M renders the
  diff via the existing HTML export and returns {html, authors}
- POST /admin/api/pads/{padId}/compact reuses the admin-UI revision
  compaction (AdminMessageHandler.DeleteRevisions)

anonymizeAuthor (GDPR, original API 1.3.1):
- Manager.AnonymizeAuthor mirrors the original ordering: sever the
  token binding first, zero name/color, null chat authorship
- POST /admin/api/author/{authorId}/anonymize endpoint
- new DataStore primitives RemoveTokenOfAuthor and ClearChatAuthorship
  on all four backends; GetChatsOfPad now LEFT JOINs so anonymized
  messages stay retrievable, GetAuthorIdsOfPadChats skips NULL authors

preAuthorize / preAuthzFailure plugin hooks:
- ported from the original webaccess Step 1: explicit permit skips
  authentication (never on /admin-auth paths), deny returns 403 unless
  a preAuthzFailure hook overrides the response, no answer defers to
  the regular flow; wired into the server middleware

Bug fixed along the way:
- adminutils.CreateRevision mutated a by-value pool copy that shared
  the caller's maps, corrupting the pad pool (Pad.Check then failed
  with "numToAttrib length does not match nextNum" and revision
  cleanup 500ed). It now works on a real deep copy (new APool.Clone);
  the dead, nil-map-writing APool.clone was removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0)

Grey Divider


Action required

1. DiffHTML mutates cached pool 🐞 Bug ☼ Reliability
Description
CreateDiffHTML passes the cached pad’s Pool into paddiff.CreateDiffAText, which mutates it via
PutAttrib; this makes a read-only endpoint modify shared pad state and can introduce request-time
data races/corruption. Because pads are globally cached and reused across requests, the mutation
persists beyond the diff request.
Code

lib/api/pad/compactDiff.go[R134-148]

+		diffAText, authors, err := paddiff.CreateDiffAText(foundPad, &foundPad.Pool, from, to)
+		if err != nil {
+			initStore.Logger.Errorf("Error creating diff atext for pad %s: %v", padId, err)
+			return c.Status(500).JSON(errors2.InternalServerError)
+		}
+
+		// Render the diff atext with the regular export-HTML pipeline (it
+		// understands the 'removed' attribute). GetPadHTML reads pad.AText when
+		// no revision is requested, so a shallow copy of the pad carrying the
+		// diff atext is passed.
+		padWithDiff := *foundPad
+		padWithDiff.AText = *diffAText
+
+		authorColors := buildAuthorColors(&foundPad.Pool, initStore.AuthorManager)
+		exporter := io2.NewExportHtml(initStore.PadManager, initStore.AuthorManager, initStore.Hooks)
Evidence
The diffHTML handler passes the cached pad’s Pool by reference to code that explicitly adds
attributes to (mutates) the supplied pool; pads are returned from a global cache as shared pointers,
so this mutates shared state across requests. The mutation happens via PutAttrib, which updates
NextNum and the underlying maps.

lib/api/pad/compactDiff.go[121-149]
lib/paddiff/paddiff.go[51-56]
lib/paddiff/paddiff.go[209-226]
lib/apool/APool.go[94-108]
lib/pad/padManager.go[69-166]
lib/models/pad/Pad.go[44-58]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`CreateDiffHTML` currently calls `paddiff.CreateDiffAText(foundPad, &foundPad.Pool, ...)`, and paddiff mutates the provided pool (`PutAttrib`). Because `foundPad` is a cached/shared pad instance, this causes a GET endpoint to mutate shared state and can create data races under concurrent requests.

## Issue Context
Pads are returned from a global cache and reused across requests; the pad struct contains an `apool.APool` with mutable maps.

## Fix
Clone the pad’s pool in `CreateDiffHTML` and use the clone consistently:
- `poolClone := foundPad.Pool.Clone()`
- call `CreateDiffAText(foundPad, &poolClone, ...)`
- set `padWithDiff.Pool = poolClone` before rendering
- build colors from the clone (`buildAuthorColors(&poolClone, ...)`)
This keeps diff generation side-effect-free and avoids concurrent mutation of cached pad state.

## Fix Focus Areas
- lib/api/pad/compactDiff.go[121-149]
- lib/paddiff/paddiff.go[51-56]
- lib/paddiff/paddiff.go[209-226]
- lib/apool/APool.go[94-108]
- lib/pad/padManager.go[69-166]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. DB errors returned as 404 🐞 Bug ≡ Correctness
Description
author.Manager.AnonymizeAuthor converts any error from Db.GetAuthor into db.AuthorNotFoundError, so
DB/query failures are misreported as “not found” and the API returns 404 instead of 500. This hides
real backend failures and breaks correct error semantics.
Code

lib/author/authorManager.go[R188-191]

+func (m *Manager) AnonymizeAuthor(authorId string) error {
+	if _, err := m.Db.GetAuthor(authorId); err != nil {
+		return errors.New(db.AuthorNotFoundError)
+	}
Evidence
AnonymizeAuthor replaces any GetAuthor error with AuthorNotFoundError. But datastore GetAuthor
methods can return query/scan errors (not just not-found), and the API handler maps
AuthorNotFoundError to a 404 response, so backend failures can be incorrectly surfaced as 404s.

lib/author/authorManager.go[188-191]
lib/db/PostgresDB.go[285-314]
lib/api/author/init.go[236-248]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Manager.AnonymizeAuthor()` currently does:
```go
if _, err := m.Db.GetAuthor(authorId); err != nil {
 return errors.New(db.AuthorNotFoundError)
}
```
This collapses all failures (including database/query errors) into "author not found".

## Issue Context
Datastore `GetAuthor` implementations can return non-not-found errors (query/scan/driver errors). The API layer maps `db.AuthorNotFoundError` to HTTP 404.

## Fix
Return the original `GetAuthor` error, or only map to `db.AuthorNotFoundError` when the underlying error is actually the not-found case.
A minimal safe change:
- `if _, err := m.Db.GetAuthor(authorId); err != nil { return err }`
If you want to keep explicit mapping, do:
- `if err.Error() == db.AuthorNotFoundError { return err } else { return err }` (i.e., don’t replace non-not-found errors).

## Fix Focus Areas
- lib/author/authorManager.go[188-191]
- lib/db/PostgresDB.go[285-314]
- lib/api/author/init.go[236-248]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@SamTV12345 SamTV12345 enabled auto-merge (squash) June 11, 2026 21:11
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add pad diff/compact & author anonymization APIs; preAuthorize hooks; fix pool cloning
✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

Walkthroughs

Description
• Add admin APIs for pad diffHTML rendering and revision compaction.
• Implement GDPR author anonymization across managers and all datastore backends.
• Add plugin preAuthorize/preAuthzFailure hooks to short-circuit web access decisions.
• Fix attribute-pool copy bug that could corrupt pads during revision creation.
Diagram
graph TD
  req["HTTP request"] --> mw["CheckAccessWithHooks"] --> preauth{"preAuthorize\npermit/deny?"}
  preauth -->|"permit/defer"| api["/admin/api routes"] --> diff["paddiff diff atext"] --> exp["Export HTML"]
  api --> compact["DeleteRevisions"] --> db[("DataStore")]
  api --> anon["AnonymizeAuthor"] --> db
  preauth -->|"deny"| fail["preAuthzFailure"]

  subgraph Legend
    direction LR
    _req["Request/middleware"] ~~~ _dec{"Decision"} ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Implement compaction as a dedicated PadManager/Store service
  • ➕ Avoids constructing a websocket/admin handler inside an HTTP handler
  • ➕ Makes compaction usable outside the WS/admin context and easier to unit test
  • ➖ Duplicates logic already implemented and parity-tested in AdminMessageHandler.DeleteRevisions
  • ➖ More refactor surface area/risk for a parity-focused PR
2. Stop at first preAuthorize answer (aCallFirst semantics)
  • ➕ Closer to upstream hook execution model and potentially faster
  • ➕ Avoids surprising 'deny wins' behavior if multiple plugins disagree
  • ➖ Requires ordering guarantees or explicit priority to be deterministic
  • ➖ Current aggregation is safer (single deny cannot be overridden accidentally)

Recommendation: Current approach is reasonable for a parity round: reuse the existing DeleteRevisions implementation to minimize behavioral drift, and aggregate preAuthorize results so any explicit deny reliably blocks access. If future work grows around compaction, consider extracting the DeleteRevisions core into a PadManager-level service to remove the handler dependency.

Grey Divider

File Changes

Enhancement (16)
init.go Add admin endpoint to anonymize an author +42/-0

Add admin endpoint to anonymize an author

• Introduces POST /admin/api/author/:authorId/anonymize with Swagger docs and response type. Calls AuthorManager.AnonymizeAuthor and maps datastore not-found into a 404.

lib/api/author/init.go


compactDiff.go Add compactPad and createDiffHTML admin pad endpoints +178/-0

Add compactPad and createDiffHTML admin pad endpoints

• Implements POST /admin/api/pads/:padId/compact (reusing AdminMessageHandler.DeleteRevisions) with keepRevisions validation. Adds GET /admin/api/pads/:padId/diffHTML that validates revision ranges, builds a diff atext via lib/paddiff, then renders HTML through the existing exporter and returns {html, authors}.

lib/api/pad/compactDiff.go


init.go Wire compact and diffHTML routes into pad admin API +2/-0

Wire compact and diffHTML routes into pad admin API

• Registers the new /pads/:padId/compact and /pads/:padId/diffHTML routes in the private admin API router.

lib/api/pad/init.go


authorManager.go Implement GDPR-style AnonymizeAuthor in AuthorManager +41/-0

Implement GDPR-style AnonymizeAuthor in AuthorManager

• Adds Manager.AnonymizeAuthor to mirror upstream ordering: remove token binding first, then zero name/color while keeping the author record, then null chat authorship. Behavior is idempotent and returns AuthorNotFound for unknown authors.

lib/author/authorManager.go


DataStore.go Extend datastore interfaces for anonymization primitives +7/-0

Extend datastore interfaces for anonymization primitives

• Adds RemoveTokenOfAuthor to AuthorMethods and ClearChatAuthorship to ChatMethods. Documents intended GDPR erasure semantics and no-op behavior when applicable.

lib/db/DataStore.go


MemoryDataStore.go Implement token removal and chat authorship clearing in memory store +21/-0

Implement token removal and chat authorship clearing in memory store

• Adds RemoveTokenOfAuthor to nil out author.Token and ClearChatAuthorship to set matching chat AuthorId values to nil while preserving messages.

lib/db/MemoryDataStore.go


MySQLDB.go Add anonymization SQL updates and preserve chats after author removal +32/-1

Add anonymization SQL updates and preserve chats after author removal

• Implements RemoveTokenOfAuthor and ClearChatAuthorship via UPDATE statements. Changes GetChatsOfPad to LEFT JOIN authors so anonymized messages still return, and filters NULLs in GetAuthorIdsOfPadChats.

lib/db/MySQLDB.go


PostgresDB.go Add anonymization SQL updates and adjust chat queries for NULL authors +18/-2

Add anonymization SQL updates and adjust chat queries for NULL authors

• Implements RemoveTokenOfAuthor and ClearChatAuthorship with UPDATE queries. Switches GetChatsOfPad join to LEFT JOIN and updates GetAuthorIdsOfPadChats to exclude NULL author IDs.

lib/db/PostgresDB.go


SQLiteDB.go Add anonymization SQL updates and adjust chat queries for NULL authors +32/-1

Add anonymization SQL updates and adjust chat queries for NULL authors

• Implements RemoveTokenOfAuthor and ClearChatAuthorship via squirrel-built UPDATEs. Uses LEFT JOIN in GetChatsOfPad and excludes NULL author IDs from GetAuthorIdsOfPadChats.

lib/db/SQLiteDB.go


HookConstants.go Define hook names for preAuthorize and preAuthzFailure +2/-0

Define hook names for preAuthorize and preAuthzFailure

• Adds hook constant strings for preAuthorize and preAuthzFailure to integrate with the hook registry.

lib/hooks/HookConstants.go


preAuthorize.go Introduce hook contexts for early permit/deny and failure override +118/-0

Introduce hook contexts for early permit/deny and failure override

• Adds PreAuthorizeContext with Permit/Deny accumulation and Decision aggregation, including special filtering for /admin-auth permits. Adds PreAuthzFailureContext to let plugins override the default 403 response with status/body/headers.

lib/hooks/events/preAuthorize.go


hook.go Add typed enqueue/execute helpers for new webaccess hooks +30/-0

Add typed enqueue/execute helpers for new webaccess hooks

• Adds Enqueue/Execute helpers for preAuthorize and preAuthzFailure, with safe type assertions to the corresponding events contexts.

lib/hooks/hook.go


webaccess.go Run preAuthorize/preAuthzFailure hooks in access middleware +34/-3

Run preAuthorize/preAuthzFailure hooks in access middleware

• Adds CheckAccessWithHooks and makes legacy CheckAccess call it with nil hooks. Middleware now executes preAuthorize to permit/defer/deny before normal auth, and allows preAuthzFailure to override the deny response.

lib/pad/webaccess.go


builder.go Add local opBuilder for changesets with pre-encoded attrib strings +64/-0

Add local opBuilder for changesets with pre-encoded attrib strings

• Introduces a small builder that can attach already-encoded attrib strings to keep/insert ops. Used by paddiff to match upstream padDiff behavior without relying on unexported changeset.Builder fields.

lib/paddiff/builder.go


paddiff.go Port Etherpad padDiff.ts into Go (diff atext composer) +523/-0

Port Etherpad padDiff.ts into Go (diff atext composer)

• Adds paddiff package that composes revision-range changesets, skips clear-authorship changesets, re-inserts deletions tagged with removed+author, and returns diff atext plus author list. Includes revision-range validation helper and uses existing changeset primitives.

lib/paddiff/paddiff.go


server.go Install CheckAccessWithHooks middleware in server init +1/-1

Install CheckAccessWithHooks middleware in server init

• Updates the global Fiber middleware to call CheckAccessWithHooks with the retrieved hook system so plugins can influence access decisions.

lib/server/server.go


Bug fix (2)
cleanup.go Deep-clone attribute pool before writing author attribution +5/-0

Deep-clone attribute pool before writing author attribution

• CreateRevision now clones the passed APool before PutAttrib when setting an author. This prevents mutating shared map state and corrupting the caller's pool/NextNum consistency during revision creation.

lib/adminutils/cleanup.go


APool.go Replace broken clone() with safe APool.Clone deep copy +24/-17

Replace broken clone() with safe APool.Clone deep copy

• Removes the unused/incorrect clone() and adds Clone() that deep-copies NumToAttrib/AttribToNum and NumToAttribRaw. Prevents subtle corruption when a struct copy shares underlying maps.

lib/apool/APool.go


Tests (5)
paddiff_test.go Unit tests for paddiff revision range and diff attribution semantics +233/-0

Unit tests for paddiff revision range and diff attribution semantics

• Adds tests covering revision-range validation, deletion reinsertion with removed attribute, multi-revision composition/author collection, skipping clear-authorship changesets, and empty-range behavior.

lib/paddiff/paddiff_test.go


author_test.go API tests for author anonymization endpoint +90/-0

API tests for author anonymization endpoint

• Adds tests for successful anonymization (token severed, name scrubbed, chat authorship nulled), 404 on unknown author, and idempotent behavior across multiple calls.

lib/test/api/author/author_test.go


pad_compact_diff_api_test.go API tests for compactPad and createDiffHTML across backends +267/-0

API tests for compactPad and createDiffHTML across backends

• Adds coverage for successful revision compaction preserving text, validation and 404 cases, and diffHTML returning HTML with removed markings and authors; includes endRev defaulting behavior.

lib/test/api/pad/pad_compact_diff_api_test.go


authorManager_test.go Manager-level tests for AnonymizeAuthor behavior +93/-0

Manager-level tests for AnonymizeAuthor behavior

• Adds tests ensuring identity scrubbing, token unlinking, chat authorship clearing, unknown-author error, and idempotency at the manager layer.

lib/test/author/authorManager_test.go


webaccess_test.go Middleware tests for preAuthorize/preAuthzFailure semantics +165/-0

Middleware tests for preAuthorize/preAuthzFailure semantics

• Adds tests verifying unchanged behavior without hooks, deny/permit precedence, admin-path permit filtering, and response override via preAuthzFailure (e.g., redirect). Includes direct Decision() semantics tests.

lib/test/pad/webaccess_test.go


Grey Divider

Qodo Logo

Comment on lines +134 to +148
diffAText, authors, err := paddiff.CreateDiffAText(foundPad, &foundPad.Pool, from, to)
if err != nil {
initStore.Logger.Errorf("Error creating diff atext for pad %s: %v", padId, err)
return c.Status(500).JSON(errors2.InternalServerError)
}

// Render the diff atext with the regular export-HTML pipeline (it
// understands the 'removed' attribute). GetPadHTML reads pad.AText when
// no revision is requested, so a shallow copy of the pad carrying the
// diff atext is passed.
padWithDiff := *foundPad
padWithDiff.AText = *diffAText

authorColors := buildAuthorColors(&foundPad.Pool, initStore.AuthorManager)
exporter := io2.NewExportHtml(initStore.PadManager, initStore.AuthorManager, initStore.Hooks)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Diffhtml mutates cached pool 🐞 Bug ☼ Reliability

CreateDiffHTML passes the cached pad’s Pool into paddiff.CreateDiffAText, which mutates it via
PutAttrib; this makes a read-only endpoint modify shared pad state and can introduce request-time
data races/corruption. Because pads are globally cached and reused across requests, the mutation
persists beyond the diff request.
Agent Prompt
## Issue description
`CreateDiffHTML` currently calls `paddiff.CreateDiffAText(foundPad, &foundPad.Pool, ...)`, and paddiff mutates the provided pool (`PutAttrib`). Because `foundPad` is a cached/shared pad instance, this causes a GET endpoint to mutate shared state and can create data races under concurrent requests.

## Issue Context
Pads are returned from a global cache and reused across requests; the pad struct contains an `apool.APool` with mutable maps.

## Fix
Clone the pad’s pool in `CreateDiffHTML` and use the clone consistently:
- `poolClone := foundPad.Pool.Clone()`
- call `CreateDiffAText(foundPad, &poolClone, ...)`
- set `padWithDiff.Pool = poolClone` before rendering
- build colors from the clone (`buildAuthorColors(&poolClone, ...)`)
This keeps diff generation side-effect-free and avoids concurrent mutation of cached pad state.

## Fix Focus Areas
- lib/api/pad/compactDiff.go[121-149]
- lib/paddiff/paddiff.go[51-56]
- lib/paddiff/paddiff.go[209-226]
- lib/apool/APool.go[94-108]
- lib/pad/padManager.go[69-166]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +188 to +191
func (m *Manager) AnonymizeAuthor(authorId string) error {
if _, err := m.Db.GetAuthor(authorId); err != nil {
return errors.New(db.AuthorNotFoundError)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Db errors returned as 404 🐞 Bug ≡ Correctness

author.Manager.AnonymizeAuthor converts any error from Db.GetAuthor into db.AuthorNotFoundError, so
DB/query failures are misreported as “not found” and the API returns 404 instead of 500. This hides
real backend failures and breaks correct error semantics.
Agent Prompt
## Issue description
`Manager.AnonymizeAuthor()` currently does:
```go
if _, err := m.Db.GetAuthor(authorId); err != nil {
  return errors.New(db.AuthorNotFoundError)
}
```
This collapses all failures (including database/query errors) into "author not found".

## Issue Context
Datastore `GetAuthor` implementations can return non-not-found errors (query/scan/driver errors). The API layer maps `db.AuthorNotFoundError` to HTTP 404.

## Fix
Return the original `GetAuthor` error, or only map to `db.AuthorNotFoundError` when the underlying error is actually the not-found case.
A minimal safe change:
- `if _, err := m.Db.GetAuthor(authorId); err != nil { return err }`
If you want to keep explicit mapping, do:
- `if err.Error() == db.AuthorNotFoundError { return err } else { return err }` (i.e., don’t replace non-not-found errors).

## Fix Focus Areas
- lib/author/authorManager.go[188-191]
- lib/db/PostgresDB.go[285-314]
- lib/api/author/init.go[236-248]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: playwright (windows-latest, amd64)

Failed stage: Run Playwright tests [❌]

Failed test name: [chromium] › specs\mobile_layout.spec.ts:31:9 › mobile layout › does not overflow horizontally on tiny mobile viewport

Failure summary:

The GitHub Action failed because the Playwright test run exited with code 1 after a failing UI test.

- Failed test: [chromium] › specs\mobile_layout.spec.ts:31:9 › mobile layout › does not overflow
horizontally on tiny mobile viewport
- Assertion failed at
playwright/specs/mobile_layout.spec.ts:45:36: popupBox.width was expected to be <= 320, but was
320.684... (also failed on retry with 320.684...).
- A second test was marked flaky: [chromium] ›
specs\indentation.spec.ts:37:9 › indentation button › keeps the indent on enter for the new line
-
Assertion failed at playwright/specs/indentation.spec.ts:55:36: expected
locator('#innerdocbody').locator('ul li') to have count 3, but it stayed at 1 until the 10000ms
timeout.
- The server logs show multiple SQLITE_BUSY (database is locked) errors (e.g.,
lib/ws/PadMessageHandler.go:327), which may have contributed to frontend test instability, but the
workflow failure is triggered by the Playwright assertion failure(s).

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

91:  Removing HTTP extra header
92:  [command]"C:\Program Files\Git\bin\git.exe" config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
93:  [command]"C:\Program Files\Git\bin\git.exe" submodule foreach --recursive "sh -c \"git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :\""
94:  Removing includeIf entries pointing to credentials config files
95:  [command]"C:\Program Files\Git\bin\git.exe" config --local --name-only --get-regexp ^includeIf\.gitdir:
96:  [command]"C:\Program Files\Git\bin\git.exe" submodule foreach --recursive "git config --local --show-origin --name-only --get-regexp remote.origin.url"
97:  [command]"C:\Program Files\Git\bin\git.exe" config --file D:\a\_temp\git-credentials-4bbf5ef3-f716-463b-af69-bd48802269c4.config http.https://github.com/.extraheader "AUTHORIZATION: basic ***"
98:  [command]"C:\Program Files\Git\bin\git.exe" config --local includeIf.gitdir:D:/a/etherpad-go/etherpad-go/.git.path D:\a\_temp\git-credentials-4bbf5ef3-f716-463b-af69-bd48802269c4.config
99:  [command]"C:\Program Files\Git\bin\git.exe" config --local includeIf.gitdir:D:/a/etherpad-go/etherpad-go/.git/worktrees/*.path D:\a\_temp\git-credentials-4bbf5ef3-f716-463b-af69-bd48802269c4.config
100:  [command]"C:\Program Files\Git\bin\git.exe" config --local includeIf.gitdir:/github/workspace/.git.path /github/runner_temp/git-credentials-4bbf5ef3-f716-463b-af69-bd48802269c4.config
101:  [command]"C:\Program Files\Git\bin\git.exe" config --local includeIf.gitdir:/github/workspace/.git/worktrees/*.path /github/runner_temp/git-credentials-4bbf5ef3-f716-463b-af69-bd48802269c4.config
102:  ##[endgroup]
103:  ##[group]Fetching the repository
104:  [command]"C:\Program Files\Git\bin\git.exe" -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules origin +refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/* +0d0421a3222f0b9d97a1898150fff889178c7c61:refs/remotes/pull/280/merge
105:  From https://github.com/ether/etherpad-go
106:  * [new branch]      125-error-reading-plugin-file            -> origin/125-error-reading-plugin-file
107:  * [new branch]      dependabot/npm_and_yarn/ui/dev-dependencies-a243c58be6 -> origin/dependabot/npm_and_yarn/ui/dev-dependencies-a243c58be6
...

530:  �[2m[WebServer] �[22mRunning migration 3: OIDC storage - create key/value table for persisted oidc state
531:  �[2m[WebServer] �[22mRunning migration 4: Create dedicated OAuth token tables
532:  �[2m[WebServer] �[22mWARN	api/init.go:24	SSO admin client is not configured, cannot start admin API
533:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/api.InitAPI
534:  �[2m[WebServer] �[22m	D:/a/etherpad-go/etherpad-go/lib/api/init.go:24
535:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/server.InitServer
536:  �[2m[WebServer] �[22m	D:/a/etherpad-go/etherpad-go/lib/server/server.go:109
537:  �[2m[WebServer] �[22mmain.main
538:  �[2m[WebServer] �[22m	D:/a/etherpad-go/etherpad-go/main.go:83
539:  �[2m[WebServer] �[22mruntime.main
540:  �[2m[WebServer] �[22m	C:/hostedtoolcache/windows/go/1.25.11/x64/src/runtime/proc.go:285
541:  �[2m[WebServer] �[22mINFO	server/server.go:230	Starting Web UI on 0.0.0.0:9001
542:  �[2m[WebServer] �[22mINFO	server/update.go:63	A new version of Etherpad Go is available! Please update to the latest version.
543:  Running 125 tests using 4 workers
544:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS34e7a590-3808-4c70-8812-0c658f7f429d socket:i2501xemI4iT4Oei3WaudRHopjbw1rCefcQVbbFHPCE IP:127.0.0.1 
545:  �[2m[WebServer] �[22mWARN	ws/PadMessageHandler.go:512	Error checking accessaccess denied: invalid author tokendatabase is locked (5) (SQLITE_BUSY)
546:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/ws.(*PadMessageHandler).HandleMessage
...

647:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS52647699-38c5-4be7-9e97-326c9efd8135 socket:tXXdJbx7CS2Xz6_v5o0gaxbh-dHtFfl1BBUwQ6WIAdo IP:127.0.0.1 
648:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS5348d91d-2fd8-4092-b1f7-a0bb04b50b7a socket:s_Fh-kzq5_wQZZHnyfK2vBPVOimn9Y1fqfMUaA3t7GQ IP:127.0.0.1 
649:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSd7211493-d547-4742-9416-eccdf022bcb5 socket:PY2yYek4wJ_0Hr3ObvAgN7X1_hjD9i-ub35eIhEcV2s IP:127.0.0.1 
650:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSd7211493-d547-4742-9416-eccdf022bcb5 socket:PY2yYek4wJ_0Hr3ObvAgN7X1_hjD9i-ub35eIhEcV2s IP:127.0.0.1 
651:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSae2561b8-3890-4227-80bb-b842346a50de socket:gNQWxA2twXEJqcm0t7ds1M0NvIKvN-AXVGZl6dUAxlE IP:127.0.0.1 
652:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS3f266816-317a-4462-ba03-b96c18338093 socket:pmAnKjv0HH6hj6uL-ZTVegw84T3gbxF533S_ONNFttE IP:127.0.0.1 
653:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSb551346b-5a54-419d-81ad-a1e0f65100cd socket:OUuC_pPdkjnvaN6kMmcGCt9tRqoaDHXwS_HuXdJeRxk IP:127.0.0.1 
654:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS5348d91d-2fd8-4092-b1f7-a0bb04b50b7a socket:s_Fh-kzq5_wQZZHnyfK2vBPVOimn9Y1fqfMUaA3t7GQ IP:127.0.0.1 
655:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSb551346b-5a54-419d-81ad-a1e0f65100cd socket:OUuC_pPdkjnvaN6kMmcGCt9tRqoaDHXwS_HuXdJeRxk IP:127.0.0.1 
656:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS3f266816-317a-4462-ba03-b96c18338093 socket:pmAnKjv0HH6hj6uL-ZTVegw84T3gbxF533S_ONNFttE IP:127.0.0.1 
657:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS8121df1c-f635-4095-9cc2-1c3b27f9ea22 socket:BvzNDpPS17hxxETLdHRuqIDr3Bmcy0rjz0Q61DaWl18 IP:127.0.0.1 
658:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS4d9967a5-4ec0-46f0-b6c1-9cfa3556c402 socket:DIafCH8s83KPm8wgDK1MFe2guGRpQCQDGJdqM-NPSJ4 IP:127.0.0.1 
659:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSb7545911-460e-4545-8dbb-6062fbf9d508 socket:i-IwdrFKMsmfizPkkN00shxAJWQTFGzditw2nW2_Hg8 IP:127.0.0.1 
660:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSb7545911-460e-4545-8dbb-6062fbf9d508 socket:i-IwdrFKMsmfizPkkN00shxAJWQTFGzditw2nW2_Hg8 IP:127.0.0.1 
661:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSe6c607eb-0d3b-42b7-b80d-9cb8f93e5d37 socket:xiXBGTRkt0bRtT6vtONDM9JcoG5hRqifKJD6vHwKiuk IP:127.0.0.1 
662:  �[2m[WebServer] �[22mWARN	ws/PadMessageHandler.go:1139	Error retrieving author for disconnect
663:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/ws.(*PadMessageHandler).HandleDisconnectOfPadClient
...

671:  �[2m[WebServer] �[22mgithub.com/gofiber/contrib/v3/websocket.New.func2.2
672:  �[2m[WebServer] �[22m	C:/Users/runneradmin/go/pkg/mod/github.com/gofiber/contrib/v3/websocket@v1.2.0/websocket.go:198
673:  �[2m[WebServer] �[22mgithub.com/fasthttp/websocket.(*FastHTTPUpgrader).Upgrade.func1
674:  �[2m[WebServer] �[22m	C:/Users/runneradmin/go/pkg/mod/github.com/fasthttp/websocket@v1.5.12/server_fasthttp.go:200
675:  �[2m[WebServer] �[22mgithub.com/valyala/fasthttp.hijackConnHandler
676:  �[2m[WebServer] �[22m	C:/Users/runneradmin/go/pkg/mod/github.com/valyala/fasthttp@v1.71.0/server.go:2703
677:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSe6c607eb-0d3b-42b7-b80d-9cb8f93e5d37 socket:XhwyVZ-0zJfYIMxWOVnijqGNO1HiMzIPtGq-qC6YE-0 IP:127.0.0.1 
678:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS91216257-d52d-4cc3-844c-0aa1b98e6bcd socket:5JUiZn1iqe0blkR3sLOFzYtJt7EwSE7H88ulsRBjZd4 IP:127.0.0.1 
679:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSe6c607eb-0d3b-42b7-b80d-9cb8f93e5d37 socket:XhwyVZ-0zJfYIMxWOVnijqGNO1HiMzIPtGq-qC6YE-0 IP:127.0.0.1 
680:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS8121df1c-f635-4095-9cc2-1c3b27f9ea22 socket:BvzNDpPS17hxxETLdHRuqIDr3Bmcy0rjz0Q61DaWl18 IP:127.0.0.1 
681:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS4d9967a5-4ec0-46f0-b6c1-9cfa3556c402 socket:DIafCH8s83KPm8wgDK1MFe2guGRpQCQDGJdqM-NPSJ4 IP:127.0.0.1 
682:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS91216257-d52d-4cc3-844c-0aa1b98e6bcd socket:5JUiZn1iqe0blkR3sLOFzYtJt7EwSE7H88ulsRBjZd4 IP:127.0.0.1 
683:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSfdc8c27b-1b30-4dbe-877e-47b831cc4cdc socket:A66yaEVuPC8irLAcPUVSoWvVGON7FcCMGyjhGhRh3-c IP:127.0.0.1 
684:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS5fe87fa3-9561-4b79-bfbb-53fd750d9048 socket:HrXSdyGEBB0cx5Q9fb3d5HK_XUDx-fr37LkCey84X4Y IP:127.0.0.1 
685:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSe1d8f782-7526-4d6d-be49-98934d7b54bc socket:qWUQt0ABjDittwkQlI7bVRSxYEl8o6LSlyOb3YV3OOs IP:127.0.0.1 
686:  �[2m[WebServer] �[22mERROR	ws/PadMessageHandler.go:327	Error appending revision: Error saving revision during append database is locked (5) (SQLITE_BUSY)
687:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/ws.(*PadMessageHandler).handleUserChanges
688:  �[2m[WebServer] �[22m	D:/a/etherpad-go/etherpad-go/lib/ws/PadMessageHandler.go:327
689:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/ws.(*ChannelOperator).AddToQueue.func1
690:  �[2m[WebServer] �[22m	D:/a/etherpad-go/etherpad-go/lib/ws/PadMessageHandler.go:80
691:  �[2m[WebServer] �[22m2026/06/11 21:24:37 error: websocket: close 1005 (no status)
692:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSfdc8c27b-1b30-4dbe-877e-47b831cc4cdc socket:A66yaEVuPC8irLAcPUVSoWvVGON7FcCMGyjhGhRh3-c IP:127.0.0.1 
...

710:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSa83c474a-b0da-4456-9e57-ca6bf401bec5 socket:ZYQCD2qo8liEh62UNA1gm9CMICEnKu0H-Js83PkASTc IP:127.0.0.1 
711:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSa83c474a-b0da-4456-9e57-ca6bf401bec5 socket:q-yn9Edmp5Z86sLz9wLt2t_rLzTZzzCMw2CDGhKPD20 IP:127.0.0.1 
712:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS88ef8118-72f1-4641-8f4a-657375be9569 socket:GpEp-_X-U-VReOYal7W-C8Cs2XzxdSzMLoy37otjp0k IP:127.0.0.1 
713:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS88ef8118-72f1-4641-8f4a-657375be9569 socket:EvUuem069W9s5OD_cjVipAfnKnMKmcapsMoUH0PHLlY IP:127.0.0.1 
714:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSa83c474a-b0da-4456-9e57-ca6bf401bec5 socket:q-yn9Edmp5Z86sLz9wLt2t_rLzTZzzCMw2CDGhKPD20 IP:127.0.0.1 
715:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS5a3b09f2-2053-459c-afbe-a499c2b0d72c socket:3FnQ7FQXLnpE18tyncEvyQHsqaHVetbTuGdpxd1DcXE IP:127.0.0.1 
716:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSc83895b5-69b8-4798-8e5f-18f58656abb1 socket:_izojd2hhBeYQ6PTK7u8L43c2Qv-iXYw7XPgjTOdzj8 IP:127.0.0.1 
717:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS5a3b09f2-2053-459c-afbe-a499c2b0d72c socket:f_xEPyBeq77X573_SPgnuFzQIJCgDohWorIkY4Mfop4 IP:127.0.0.1 
718:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS5a3b09f2-2053-459c-afbe-a499c2b0d72c socket:f_xEPyBeq77X573_SPgnuFzQIJCgDohWorIkY4Mfop4 IP:127.0.0.1 
719:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSb440d1c6-7e7f-46a5-909f-3e20df5ebad7 socket:gWGzM6eOp2JwzwOhbEAVuX3NZNE_D6MBUd6Hi_71qj4 IP:127.0.0.1 
720:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS88ef8118-72f1-4641-8f4a-657375be9569 socket:EvUuem069W9s5OD_cjVipAfnKnMKmcapsMoUH0PHLlY IP:127.0.0.1 
721:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS1fbc5637-4d34-4c8a-8655-61ae1ba1451b socket:wIS71tneQZqXzVYInQS5aBS0davziMWUo3M49M-ILBQ IP:127.0.0.1 
722:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS88ef8118-72f1-4641-8f4a-657375be9569 socket:JHEXrsZDG523ftmHo3Bi83WafcbyDFnPFf3FotGfNM8 IP:127.0.0.1 
723:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS88ef8118-72f1-4641-8f4a-657375be9569 socket:JHEXrsZDG523ftmHo3Bi83WafcbyDFnPFf3FotGfNM8 IP:127.0.0.1 
724:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS1fbc5637-4d34-4c8a-8655-61ae1ba1451b socket:wIS71tneQZqXzVYInQS5aBS0davziMWUo3M49M-ILBQ IP:127.0.0.1 
725:  ···············°··········°···············×··±::error file=playwright\specs\indentation.spec.ts,title=[chromium] › specs\indentation.spec.ts:37:9 › indentation button › keeps the indent on enter for the new line,line=55,col=36::  1) [chromium] › specs\indentation.spec.ts:37:9 › indentation button › keeps the indent on enter for the new line %0A    Error: expect(locator).toHaveCount(expected) failed%0A%0A    Locator:  locator('#innerdocbody').locator('ul li')%0A    Expected: 3%0A    Received: 1%0A    Timeout:  10000ms%0A%0A    Call log:%0A      - Expect "toHaveCount" with timeout 10000ms%0A      - waiting for locator('#innerdocbody').locator('ul li')%0A        24 × locator resolved to 1 element%0A           - unexpected value "1"%0A%0A%0A      53 |         const hasULElement = padBody.locator('ul li')%0A      54 |%0A    > 55 |         await expect(hasULElement).toHaveCount(3);%0A         |                                    ^%0A      56 |         await expect($newSecondLine).toHaveText('line 2');%0A      57 |     });%0A      58 |%0A        at D:\a\etherpad-go\etherpad-go\playwright\specs\indentation.spec.ts:55:36
726:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSc83895b5-69b8-4798-8e5f-18f58656abb1 socket:_izojd2hhBeYQ6PTK7u8L43c2Qv-iXYw7XPgjTOdzj8 IP:127.0.0.1 
...

745:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS81ee6e1d-7ae9-46cb-911d-54b415d530a7 socket:LgcihTLYY9COjeig7M-iYXvT026XwLDUdeb6YuALB04 IP:127.0.0.1 
746:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS39601f7b-4796-4145-b0cf-913d02b2ff23 socket:D5UPjB1OpsR4EkOPJUIFkPK1oz3GNb8WS014dwqVPLA IP:127.0.0.1 
747:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS40c59aa4-9b13-48dd-9997-477a8b355e30 socket:sVoyNURWMW_-_LgZZEaU6tHKYpnmplvVwVgcjKyFGtE IP:127.0.0.1 
748:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS0bf00845-1f38-493a-a4a6-8694b4773fc7 socket:jS1y5MqMiDvtjIG3dTeQvXc4vIY0eliu0wt6A_W4EJQ IP:127.0.0.1 
749:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS81ee6e1d-7ae9-46cb-911d-54b415d530a7 socket:LgcihTLYY9COjeig7M-iYXvT026XwLDUdeb6YuALB04 IP:127.0.0.1 
750:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS0bf00845-1f38-493a-a4a6-8694b4773fc7 socket:jS1y5MqMiDvtjIG3dTeQvXc4vIY0eliu0wt6A_W4EJQ IP:127.0.0.1 
751:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS3e7b6ff7-6204-4b00-b161-d1a06f4832a9 socket:u_xLu64quXGYL6Hj1_jGdN1wmSKGO2t-icJg5IF3P-U IP:127.0.0.1 
752:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSa4773e9c-b53d-4955-b1aa-baf15841cf5e socket:smVur8CbGv83qHYJfcQF16NrLypetAvkjmh3cuYDapc IP:127.0.0.1 
753:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS3e7b6ff7-6204-4b00-b161-d1a06f4832a9 socket:u_xLu64quXGYL6Hj1_jGdN1wmSKGO2t-icJg5IF3P-U IP:127.0.0.1 
754:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS0da9764c-7e9f-4298-8108-53729afc1df9 socket:dpORElAIXybURxk1KGXUXxKe5hzJkRaUeMy_OopSyzI IP:127.0.0.1 
755:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS39601f7b-4796-4145-b0cf-913d02b2ff23 socket:D5UPjB1OpsR4EkOPJUIFkPK1oz3GNb8WS014dwqVPLA IP:127.0.0.1 
756:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS0da9764c-7e9f-4298-8108-53729afc1df9 socket:dpORElAIXybURxk1KGXUXxKe5hzJkRaUeMy_OopSyzI IP:127.0.0.1 
757:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSa4773e9c-b53d-4955-b1aa-baf15841cf5e socket:smVur8CbGv83qHYJfcQF16NrLypetAvkjmh3cuYDapc IP:127.0.0.1 
758:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS09df1486-c382-4684-9270-43551389f1d8 socket:IX0s-ZpuIki-ZP4nkoWyhIBNnL6tJBXIxStuj94qp5I IP:127.0.0.1 
759:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS48215e1d-707f-47e4-9fe8-c7f05d6b7877 socket:vwRzdLldEeH1Fmj_dZZLwS67NKh2TLNgvBDf2Nuqoww IP:127.0.0.1 
760:  �[2m[WebServer] �[22mWARN	ws/PadMessageHandler.go:512	Error checking accessaccess denied: invalid author tokendatabase is locked (5) (SQLITE_BUSY)
761:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/ws.(*PadMessageHandler).HandleMessage
...

764:  �[2m[WebServer] �[22m	D:/a/etherpad-go/etherpad-go/lib/ws/client.go:147
765:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/ws.ServeWs
766:  �[2m[WebServer] �[22m	D:/a/etherpad-go/etherpad-go/lib/ws/client.go:266
767:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/server.InitServer.func3
768:  �[2m[WebServer] �[22m	D:/a/etherpad-go/etherpad-go/lib/server/server.go:175
769:  �[2m[WebServer] �[22mgithub.com/gofiber/contrib/v3/websocket.New.func2.2
770:  �[2m[WebServer] �[22m	C:/Users/runneradmin/go/pkg/mod/github.com/gofiber/contrib/v3/websocket@v1.2.0/websocket.go:198
771:  �[2m[WebServer] �[22mgithub.com/fasthttp/websocket.(*FastHTTPUpgrader).Upgrade.func1
772:  �[2m[WebServer] �[22m	C:/Users/runneradmin/go/pkg/mod/github.com/fasthttp/websocket@v1.5.12/server_fasthttp.go:200
773:  �[2m[WebServer] �[22mgithub.com/valyala/fasthttp.hijackConnHandler
774:  �[2m[WebServer] �[22m	C:/Users/runneradmin/go/pkg/mod/github.com/valyala/fasthttp@v1.71.0/server.go:2703
775:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS48215e1d-707f-47e4-9fe8-c7f05d6b7877 socket:vwRzdLldEeH1Fmj_dZZLwS67NKh2TLNgvBDf2Nuqoww IP:127.0.0.1 
776:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS5fcbbb6d-d3be-44a4-a580-0c87b363f69c socket:d-0dkh9CyXqoCpr3mSKANd1sdUdX0H_rj0Kx_aOjEaA IP:127.0.0.1 
777:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS09df1486-c382-4684-9270-43551389f1d8 socket:IX0s-ZpuIki-ZP4nkoWyhIBNnL6tJBXIxStuj94qp5I IP:127.0.0.1 
778:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS5fcbbb6d-d3be-44a4-a580-0c87b363f69c socket:d-0dkh9CyXqoCpr3mSKANd1sdUdX0H_rj0Kx_aOjEaA IP:127.0.0.1 
779:  ·····×·······F::error file=playwright\specs\mobile_layout.spec.ts,title=[chromium] › specs\mobile_layout.spec.ts:31:9 › mobile layout › does not overflow horizontally on tiny mobile viewport,line=45,col=36::  2) [chromium] › specs\mobile_layout.spec.ts:31:9 › mobile layout › does not overflow horizontally on tiny mobile viewport %0A    Error: expect(received).toBeLessThanOrEqual(expected)%0A%0A    Expected: <= 320%0A    Received:    320.6840362548828%0A%0A      43 |         expect(popupBox).not.toBeNull();%0A      44 |         if (popupBox) {%0A    > 45 |             expect(popupBox.width).toBeLessThanOrEqual(320);%0A         |                                    ^%0A      46 |         }%0A      47 |     });%0A      48 |%0A        at D:\a\etherpad-go\etherpad-go\playwright\specs\mobile_layout.spec.ts:45:36
780:  ##[error]  2) [chromium] › specs\mobile_layout.spec.ts:31:9 › mobile layout › does not overflow horizontally on tiny mobile viewport 
781:  
782:      Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
783:      Error: expect(received).toBeLessThanOrEqual(expected)
784:  
...

854:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS1828d9dc-f97c-4ea6-a760-6943531b24b3 socket:MHn8sMYMVYTV-UUL_RTwkPHOR3TFAMY0SnGPodZD49U IP:127.0.0.1 
855:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS61791ad4-3ba2-4f60-b180-d4d892611bc7 socket:DHC7NYKAlVC2rCFUU3K7gsE24hcFi0lcwdSeWjrPmuc IP:127.0.0.1 
856:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS6d5da48f-6e06-450a-ba6d-dbc185496a5d socket:ObLDQTPcJVl_JiEnbqk1QJflwMWM3ntqXOjctxs27mE IP:127.0.0.1 
857:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSab9db113-29a8-464f-9e63-3dee0c4a7c9b socket:yAsifL5cW6PXWnIg8BhzulIqF-Ld8jBly_KjJdSQ3Xc IP:127.0.0.1 
858:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS67f3dbc7-17a7-478c-8728-4d395a333971 socket:oyQvp8vw_RXm2Qfsq8D5Gwo9q-Tyciv1dmdgLDYDAf4 IP:127.0.0.1 
859:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS15944040-6769-4f70-af4e-627be72b1ce4 socket:AtGZIBAkcMLRI1shtf5dxFac1PNL6vHQ72_tvUz7Y34 IP:127.0.0.1 
860:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSab9db113-29a8-464f-9e63-3dee0c4a7c9b socket:yAsifL5cW6PXWnIg8BhzulIqF-Ld8jBly_KjJdSQ3Xc IP:127.0.0.1 
861:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS67f3dbc7-17a7-478c-8728-4d395a333971 socket:oyQvp8vw_RXm2Qfsq8D5Gwo9q-Tyciv1dmdgLDYDAf4 IP:127.0.0.1 
862:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS15944040-6769-4f70-af4e-627be72b1ce4 socket:AtGZIBAkcMLRI1shtf5dxFac1PNL6vHQ72_tvUz7Y34 IP:127.0.0.1 
863:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS2ecc8240-1ddd-4c97-a9e5-2128484e2bbe socket:HEAQaNHg6q64ZfJSo0uJyHtsryNIglneJ47tUo7Ql5w IP:127.0.0.1 
864:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSacee7dac-3e38-4c69-9cf5-c0c7cfd280e5 socket:9BhvYlZgu4yNjRp9-nsmF74V08Nqna1WAyZjY5LNxQA IP:127.0.0.1 
865:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS21962a2b-53f9-47d2-a69b-78475e8cc5f9 socket:AOH95fHZG7Vjn_ejOABCWhQexqQnHaFd0QOtmLNT6tg IP:127.0.0.1 
866:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS2ecc8240-1ddd-4c97-a9e5-2128484e2bbe socket:HEAQaNHg6q64ZfJSo0uJyHtsryNIglneJ47tUo7Ql5w IP:127.0.0.1 
867:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSacee7dac-3e38-4c69-9cf5-c0c7cfd280e5 socket:9BhvYlZgu4yNjRp9-nsmF74V08Nqna1WAyZjY5LNxQA IP:127.0.0.1 
868:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS52a4d7e2-a51b-4084-a87d-e5bd10e33fa6 socket:qh0jp2NIt4BpE_Hc0jQWDxOZVzvOCsYadNUKaJmfDbI IP:127.0.0.1 
869:  �[2m[WebServer] �[22mWARN	ws/PadMessageHandler.go:1139	Error retrieving author for disconnect
870:  �[2m[WebServer] �[22mgithub.com/ether/etherpad-go/lib/ws.(*PadMessageHandler).HandleDisconnectOfPadClient
...

944:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS0539741f-57bb-418b-927d-aa63b803df58 socket:BuIdwLWGdIRcFio8lSF2-pi53buWs2u_RAkVN5fk5EM IP:127.0.0.1 
945:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS47c458fd-a424-41fd-ac0d-d8059f9b9825 socket:otvFoGgNNhNfUF_kkzFpWZeJJyJARIgeCbeC7r4j0iY IP:127.0.0.1 
946:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS6b96ec22-e1bd-464a-b9ad-84fc743867c9 socket:-PsO004XsJ9TS4hmHjArjdi8XjrDp6W4dh7m0LKo-CM IP:127.0.0.1 
947:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS9e467aa2-078b-44c1-ac8c-86a5160eb584 socket:8j0GTSwjxEC3Ct0JzETIA5SxupISIYBeiUSaA3mVkq8 IP:127.0.0.1 
948:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS6b96ec22-e1bd-464a-b9ad-84fc743867c9 socket:-PsO004XsJ9TS4hmHjArjdi8XjrDp6W4dh7m0LKo-CM IP:127.0.0.1 
949:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS0539741f-57bb-418b-927d-aa63b803df58 socket:BuIdwLWGdIRcFio8lSF2-pi53buWs2u_RAkVN5fk5EM IP:127.0.0.1 
950:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS47c458fd-a424-41fd-ac0d-d8059f9b9825 socket:otvFoGgNNhNfUF_kkzFpWZeJJyJARIgeCbeC7r4j0iY IP:127.0.0.1 
951:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTScc5ae4b0-307e-4176-8bf4-74f3d26d92a7 socket:BPisLo0sKFQmoyB09majRzuHSn5kdK-a01b7gdgV7MA IP:127.0.0.1 
952:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTSdf95f099-4399-4b70-8844-9ef81a6ba7a9 socket:Abnw07JbIEBsNL9VNbCVSYrzg1iRlJL4O05tPV6cNaY IP:127.0.0.1 
953:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1220	[CREATE] pad:FRONTEND_TESTS2fddefb3-32b6-4f3f-8cdd-7056c2e0a824 socket:wXo5iq1AS8sQz16flv3Fv6tXq20X49ieJqQjw3ZBPMc IP:127.0.0.1 
954:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTS2fddefb3-32b6-4f3f-8cdd-7056c2e0a824 socket:wXo5iq1AS8sQz16flv3Fv6tXq20X49ieJqQjw3ZBPMc IP:127.0.0.1 
955:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTScc5ae4b0-307e-4176-8bf4-74f3d26d92a7 socket:BPisLo0sKFQmoyB09majRzuHSn5kdK-a01b7gdgV7MA IP:127.0.0.1 
956:  �[2m[WebServer] �[22mINFO	ws/PadMessageHandler.go:1133	[LEAVE] pad:FRONTEND_TESTSdf95f099-4399-4b70-8844-9ef81a6ba7a9 socket:Abnw07JbIEBsNL9VNbCVSYrzg1iRlJL4O05tPV6cNaY IP:127.0.0.1 
957:  ···············································
958:  1) [chromium] › specs\mobile_layout.spec.ts:31:9 › mobile layout › does not overflow horizontally on tiny mobile viewport 
959:  Error: �[2mexpect(�[22m�[31mreceived�[39m�[2m).�[22mtoBeLessThanOrEqual�[2m(�[22m�[32mexpected�[39m�[2m)�[22m
960:  Expected: <= �[32m320�[39m
961:  Received:    �[31m320.6840362548828�[39m
962:  43 |         expect(popupBox).not.toBeNull();
963:  44 |         if (popupBox) {
964:  > 45 |             expect(popupBox.width).toBeLessThanOrEqual(320);
965:  |                                    ^
966:  46 |         }
967:  47 |     });
968:  48 |
969:  at D:\a\etherpad-go\etherpad-go\playwright\specs\mobile_layout.spec.ts:45:36
970:  attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────
971:  test-results\specs-mobile_layout-mobile-67074-lly-on-tiny-mobile-viewport-chromium\test-failed-1.png
972:  ────────────────────────────────────────────────────────────────────────────────────────────────
973:  Error Context: test-results\specs-mobile_layout-mobile-67074-lly-on-tiny-mobile-viewport-chromium\error-context.md
974:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
975:  Error: �[2mexpect(�[22m�[31mreceived�[39m�[2m).�[22mtoBeLessThanOrEqual�[2m(�[22m�[32mexpected�[39m�[2m)�[22m
976:  Expected: <= �[32m320�[39m
977:  Received:    �[31m320.68426513671875�[39m
978:  43 |         expect(popupBox).not.toBeNull();
979:  44 |         if (popupBox) {
980:  > 45 |             expect(popupBox.width).toBeLessThanOrEqual(320);
981:  |                                    ^
982:  46 |         }
983:  47 |     });
984:  48 |
985:  at D:\a\etherpad-go\etherpad-go\playwright\specs\mobile_layout.spec.ts:45:36
986:  attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────
987:  test-results\specs-mobile_layout-mobile-67074-lly-on-tiny-mobile-viewport-chromium-retry1\test-failed-1.png
988:  ────────────────────────────────────────────────────────────────────────────────────────────────
989:  Error Context: test-results\specs-mobile_layout-mobile-67074-lly-on-tiny-mobile-viewport-chromium-retry1\error-context.md
990:  attachment #3: trace (application/zip) ─────────────────────────────────────────────────────────
991:  test-results\specs-mobile_layout-mobile-67074-lly-on-tiny-mobile-viewport-chromium-retry1\trace.zip
992:  Usage:
993:  pnpm exec playwright show-trace test-results\specs-mobile_layout-mobile-67074-lly-on-tiny-mobile-viewport-chromium-retry1\trace.zip
994:  ────────────────────────────────────────────────────────────────────────────────────────────────
995:  2) [chromium] › specs\indentation.spec.ts:37:9 › indentation button › keeps the indent on enter for the new line 
996:  Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoHaveCount�[2m(�[22m�[32mexpected�[39m�[2m)�[22m failed
997:  Locator:  locator('#innerdocbody').locator('ul li')
...

1000:  Timeout:  10000ms
1001:  Call log:
1002:  �[2m  - Expect "toHaveCount" with timeout 10000ms�[22m
1003:  �[2m  - waiting for locator('#innerdocbody').locator('ul li')�[22m
1004:  �[2m    24 × locator resolved to 1 element�[22m
1005:  �[2m       - unexpected value "1"�[22m
1006:  53 |         const hasULElement = padBody.locator('ul li')
1007:  54 |
1008:  > 55 |         await expect(hasULElement).toHaveCount(3);
1009:  |                                    ^
1010:  56 |         await expect($newSecondLine).toHaveText('line 2');
1011:  57 |     });
1012:  58 |
1013:  at D:\a\etherpad-go\etherpad-go\playwright\specs\indentation.spec.ts:55:36
1014:  attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────
1015:  test-results\specs-indentation-indentat-93f3a-t-on-enter-for-the-new-line-chromium\test-failed-1.png
1016:  ────────────────────────────────────────────────────────────────────────────────────────────────
1017:  Error Context: test-results\specs-indentation-indentat-93f3a-t-on-enter-for-the-new-line-chromium\error-context.md
1018:  1 failed
1019:  [chromium] › specs\mobile_layout.spec.ts:31:9 › mobile layout › does not overflow horizontally on tiny mobile viewport 
1020:  1 flaky
1021:  [chromium] › specs\indentation.spec.ts:37:9 › indentation button › keeps the indent on enter for the new line 
1022:  4 skipped
1023:  119 passed (1.9m)
1024:  ##[notice]  1 failed
1025:      [chromium] › specs\mobile_layout.spec.ts:31:9 › mobile layout › does not overflow horizontally on tiny mobile viewport 
1026:    1 flaky
1027:      [chromium] › specs\indentation.spec.ts:37:9 › indentation button › keeps the indent on enter for the new line 
1028:    4 skipped
1029:    119 passed (1.9m)
1030:  [ELIFECYCLE] Test failed. See above for more details.
1031:  ##[error]Process completed with exit code 1.
1032:  ##[group]Run actions/upload-artifact@v7

@SamTV12345 SamTV12345 disabled auto-merge June 11, 2026 21:26
@SamTV12345 SamTV12345 merged commit 0d817c6 into main Jun 11, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant