- Implement `test_convert_channels_rewrites_account_action_suffixes` to ensure
that account action suffixes are correctly rewritten to user-scoped channels.
- Add `test_convert_channels_drops_account_actions_for_guest` to verify that
account actions are dropped for guests without a user ID.
- Introduce `test_from_payload_does_not_suffix_account_for_nested_user_events`
to confirm that nested user events do not leak action suffixes onto account channels.
Raises `phpstan.neon` level from 3 to 4 and fixes the 549 new errors
that level 4 surfaces across 157 files. Fixes are root-cause — no
`@phpstan-ignore`, no `@var` casts, no baseline entries, no widened
types. A handful of latent bugs were fixed along the way:
- `app/controllers/general.php`: path-traversal guard was negating
`\substr(...)` before the strict comparison (`!\substr(...) === $base`
was always `false === $base`). Rewritten as `\substr(...) !== $base`.
- `src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php`
and `.../TablesDB/Logs/XList.php`: were importing the raw Matomo
`DeviceDetector` (whose `getDevice()` returns `?int`) but treating the
result as an array with `deviceName/deviceBrand/deviceModel` keys.
Swapped to `Appwrite\Detector\Detector`, matching the wrapper already
used a few lines below for `$os`/`$client`.
- `src/Appwrite/Platform/Modules/Functions/Workers/Builds.php`: a match
key was checking `$resourceKey === 'functions'` when `$resourceKey`
is `'functionId'|'siteId'` — always false. Switched to the intended
`$resource->getCollection() === 'functions'` check.
- `src/Appwrite/OpenSSL/OpenSSL.php`: `encrypt()` return type tightened
to `string|false` to match `openssl_encrypt`; this lets callers'
`=== false` error handling remain meaningful.
- `app/controllers/api/messaging.php`: removed a dead
`array_key_exists('from', [])` branch in the Msg91 provider (empty
array literal; branch was unreachable).
Large cleanup categories across the 549 fixes:
- Removed redundant `?? default` on array offsets and expressions that
PHPStan now knows are non-nullable.
- Removed unreachable statements (mostly `return;` after `throw` or
`markTestSkipped()`).
- Removed redundant `is_array`/`is_string`/`is_bool`/`instanceof` checks
on already-narrowed types.
- Added `default =>` arms (or throwing arms) to non-exhaustive matches
on `string`/`mixed` input.
- Removed dead `$document === false` branches where method return types
were tightened to non-nullable `Document`.
- Removed unused properties (`$version` on Etsy/Zoom OAuth2, `$paths` on
Installer State, `$source` on MigrationsWorker, `$account2` on two
GraphQL auth tests), unused traits (`ApiVectorsDB`, `DatabaseFixture`),
and an unused `cleanupStaleExecutions` task method.
- Replaced `assertTrue(true)` and redundant `assertIsArray`/`assertIsString`/
`assertNotNull` assertions with `addToAssertionCount(1)` or
`assertNotEmpty` where the runtime type was already known.
- Bump utopia-php/database from 5.3.17 to 5.3.19
- Remove invalid (int) cast on tenant sequence in shared tables mode
- Fix DSN construction for documentsdb/vectorsdb: filter empty strings
from explode(), skip pool filtering when shared tables env vars unset,
fail fast when no pool found
- Use dedicated mode for separate database pools in getDatabasesDB
since shared tables can't work across engines (PostgreSQL integer
_tenant vs MongoDB UUID tenant). Auto-init schema on first use.
- Add documentsdb/vectorsdb shared tables env vars to CI workflow
- Fix testChannelTablesDBRowUpdate race condition with deterministic
event drain loop
The timeout parameters were accidentally removed in daaf1d7f77, causing
the assertEventually to use the default timeout which is too short for
deployment builds in CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The root cause (PostgreSQL duplicate index race condition) has been
fixed in the database library, so the timeout workarounds are no
longer needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The assertEventually call inside Coroutine\run() causes a fatal PHP
error (exit 255) when it times out, killing the entire test process.
Moving DB/collection/attribute setup and polling outside the coroutine
ensures PHPUnit can properly handle assertion failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PostgreSQL adapter throws non-Duplicate exceptions when a table/index
already exists during concurrent createCollection. Catch Throwable and
attempt metadata-only creation as fallback. Also increase Realtime
coroutine attribute polling timeout from 30s to 60s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Realtime E2E tests were taking 24+ minutes due to intentional timeout waits. Many tests verify event filtering by expecting a TimeoutException, and each was waiting 45 seconds.
Changes:
- Reduce default WebSocket timeout from 45s to 2s in RealtimeBase
- Add optional timeout parameter to getWebsocket() methods
- Use longer timeouts (5-10s) for tests that legitimately wait for slow operations (function executions, test channel events)
- Use named parameters for improved readability
Performance impact:
- Before: 24:07 minutes (1,447 seconds)
- After: 1:09 minutes (69 seconds)
- Speedup: ~21x faster
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>