{'edge': {'expired refresh': (401,
{},
{'error': {'code': 'invalid_refresh_token',
'message': 'Refresh token is invalid or expired'}}),
('DELETE', '/app.js'): (405,
{'allow': 'GET, HEAD',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('DELETE', '/health'): (405,
{'allow': 'GET, HEAD',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('DELETE', '/v1/auth/login'): (405,
{'allow': 'GET, HEAD, POST',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('DELETE', '/v1/me'): (405,
{'allow': 'GET, HEAD',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('DELETE', '/v1/sync'): (405,
{'allow': 'GET, HEAD, POST',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('HEAD', '/app.js'): (200, {'content-type': 'text/javascript; charset=utf-8'}, b''),
('HEAD', '/health'): (200, {'content-type': 'application/json; charset=utf-8'}, b''),
('HEAD', '/v1/auth/login'): (404,
{'content-type': 'application/json; charset=utf-8'},
b''),
('HEAD', '/v1/me'): (401, {'content-type': 'application/json; charset=utf-8'}, b''),
('HEAD', '/v1/sync'): (404, {'content-type': 'application/json; charset=utf-8'}, b''),
('PATCH', '/app.js'): (405,
{'allow': 'GET, HEAD',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('PATCH', '/health'): (405,
{'allow': 'GET, HEAD',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('PATCH', '/v1/auth/login'): (405,
{'allow': 'GET, HEAD, POST',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('PATCH', '/v1/me'): (405,
{'allow': 'GET, HEAD', 'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('PATCH', '/v1/sync'): (405,
{'allow': 'GET, HEAD, POST',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('POST', '/app.js'): (405,
{'allow': 'GET, HEAD', 'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('POST', '/health'): (405,
{'allow': 'GET, HEAD', 'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('POST', '/v1/auth/login'): (400,
{'content-type': 'application/json; charset=utf-8'},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('POST', '/v1/me'): (405,
{'allow': 'GET, HEAD', 'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('POST', '/v1/sync'): (401,
{'content-type': 'application/json; charset=utf-8'},
{'error': {'code': 'authorization_required',
'message': 'Authorization Bearer token is required'}}),
('PUT', '/app.js'): (405,
{'allow': 'GET, HEAD', 'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('PUT', '/health'): (405,
{'allow': 'GET, HEAD', 'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('PUT', '/v1/auth/login'): (405,
{'allow': 'GET, HEAD, POST',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('PUT', '/v1/me'): (405,
{'allow': 'GET, HEAD', 'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('PUT', '/v1/sync'): (405,
{'allow': 'GET, HEAD, POST',
'content-type': 'text/plain; charset=utf-8'},
b'Method Not Allowed\n'),
('bearer', ''): (401,
{},
{'error': {'code': 'authorization_required',
'message': 'Authorization Bearer token is required'}}),
('bearer', 'Basic bad'): (401,
{},
{'error': {'code': 'authorization_required',
'message': 'Authorization Bearer token is '
'required'}}),
('bearer', 'Bearer '): (401,
{},
{'error': {'code': 'authorization_required',
'message': 'Authorization Bearer token is required'}}),
('bearer', 'Bearer bad'): (401,
{},
{'error': {'code': 'invalid_token',
'message': 'Access token is invalid or expired'}}),
('bearer', 'bearer bad'): (401,
{},
{'error': {'code': 'authorization_required',
'message': 'Authorization Bearer token is '
'required'}}),
('login', 0): (401,
{},
{'error': {'code': 'invalid_credentials',
'message': 'Email or password is incorrect'}}),
('login', 1): (401,
{},
{'error': {'code': 'invalid_credentials',
'message': 'Email or password is incorrect'}}),
('login', 2): (401,
{},
{'error': {'code': 'invalid_credentials',
'message': 'Email or password is incorrect'}}),
('login', 3): (401,
{},
{'error': {'code': 'invalid_credentials',
'message': 'Email or password is incorrect'}}),
('login', 4): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('login', 5): (401,
{},
{'error': {'code': 'invalid_credentials',
'message': 'Email or password is incorrect'}}),
('login', 6): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('logout JSON', 0): (204, {}, b''),
('logout JSON', 1): (204, {}, b''),
('logout JSON', 2): (204, {}, b''),
('logout JSON', 3): (204, {}, b''),
('logout JSON', 4): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('logout JSON', 5): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('password boundary', 0): 201,
('password boundary', 1): 201,
('password boundary', 2): (400,
{},
{'error': {'code': 'invalid_password',
'message': 'password is too long'}}),
('path', '/%2e%2e/app.js'): (200,
{'cache-control': 'public, max-age=3600',
'content-type': 'text/javascript; charset=utf-8'},
('web asset', 'app.js')),
('path', '/%61pp.js'): (200,
{'cache-control': 'public, max-age=3600',
'content-type': 'text/javascript; charset=utf-8'},
('web asset', 'app.js')),
('path', '//app.js'): (307,
{'content-type': 'text/html; charset=utf-8',
'location': '/app.js'},
b'Temporary Redirect.\n\n'),
('path', '/a/../app.js'): (307,
{'content-type': 'text/html; charset=utf-8',
'location': '/app.js'},
b'Temporary Redirect.\n\n'),
('path', '/app.js/'): (301,
{'cache-control': 'public, max-age=3600', 'location': '../app.js'},
b''),
('path', '/index.html'): (301, {'cache-control': 'no-cache', 'location': './'}, b''),
('path', '/v1'): (404,
{'content-type': 'text/plain; charset=utf-8'},
b'404 page not found\n'),
('path', '/v1/'): (404,
{'cache-control': 'no-store',
'content-type': 'application/json; charset=utf-8'},
{'error': {'code': 'not_found', 'message': 'Endpoint not found'}}),
('path', '/v1/../app.js'): (307,
{'cache-control': 'no-store',
'content-type': 'text/html; charset=utf-8',
'location': '/app.js'},
b'Temporary Redirect.\n\n'),
('path', '/v1//me'): (307,
{'cache-control': 'no-store',
'content-type': 'text/html; charset=utf-8',
'location': '/v1/me'},
b'Temporary Redirect.\n\n'),
('range', 'bytes=-10'): (206,
{'accept-ranges': 'bytes',
'content-range': 'bytes 1112-1121/1122',
'content-type': 'image/svg+xml'},
b'/>\n\n'),
('range', 'bytes=0-0,2-2'): (200,
{'accept-ranges': 'bytes', 'content-type': 'image/svg+xml'},
('web asset', 'icon.svg')),
('range', 'bytes=0-9'): (206,
{'accept-ranges': 'bytes',
'content-range': 'bytes 0-9/1122',
'content-type': 'image/svg+xml'},
b'\n'),
('range', 'bytes=10-1'): (416,
{'content-type': 'text/plain; charset=utf-8'},
b'invalid range\n'),
('range', 'bytes=99999999-'): (416,
{'content-range': 'bytes */1122',
'content-type': 'text/plain; charset=utf-8'},
b'invalid range: failed to overlap\n'),
('range', 'bytes=bad'): (416,
{'content-type': 'text/plain; charset=utf-8'},
b'invalid range\n'),
('range', 'items=0-1'): (416,
{'content-type': 'text/plain; charset=utf-8'},
b'invalid range\n'),
('raw sync', 0): (200, {}, {'changes': [], 'cursor': 0, 'hasMore': False}),
('raw sync', 1): (200, {}, {'changes': [], 'cursor': 0, 'hasMore': False}),
('raw sync', 2): (200, {}, {'changes': [], 'cursor': 0, 'hasMore': False}),
('raw sync', 3): (200, {}, {'changes': [], 'cursor': 0, 'hasMore': False}),
('raw sync', 4): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('raw sync', 5): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('raw sync', 6): (400,
{},
{'error': {'code': 'invalid_change',
'message': 'entity must be transaction, category, or '
'budget'}}),
('raw sync', 7): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('raw sync', 8): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('raw sync', 9): (200, {}, {'changes': [], 'cursor': 0, 'hasMore': False}),
('raw sync', 10): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('refresh JSON', 0): (400,
{},
{'error': {'code': 'refresh_token_required',
'message': 'Refresh token is required'}}),
('refresh JSON', 1): (400,
{},
{'error': {'code': 'refresh_token_required',
'message': 'Refresh token is required'}}),
('refresh JSON', 2): (400,
{},
{'error': {'code': 'refresh_token_required',
'message': 'Refresh token is required'}}),
('refresh JSON', 3): (401,
{},
{'error': {'code': 'invalid_refresh_token',
'message': 'Refresh token is invalid or expired'}}),
('refresh JSON', 4): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('refresh JSON', 5): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('register', 0): (400,
{},
{'error': {'code': 'invalid_email',
'message': 'Enter a valid email address'}}),
('register', 1): (400,
{},
{'error': {'code': 'invalid_email',
'message': 'Enter a valid email address'}}),
('register', 2): (400,
{},
{'error': {'code': 'invalid_email',
'message': 'Enter a valid email address'}}),
('register', 3): (400,
{},
{'error': {'code': 'invalid_email',
'message': 'Enter a valid email address'}}),
('register', 4): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('register', 5): (400,
{},
{'error': {'code': 'invalid_email',
'message': 'Enter a valid email address'}}),
('register', 6): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('signed claims', 0): (200, {}, {'email': 'e', 'id': 'u'}),
('signed claims', 1): (200, {}, {'email': 'e', 'id': 'u'}),
('signed claims', 2): (200, {}, {'email': '', 'id': 'u'}),
('signed claims', 3): (200, {}, {'email': '', 'id': 'u'}),
('signed claims', 4): (401,
{},
{'error': {'code': 'invalid_token',
'message': 'Access token is invalid or expired'}}),
('signed claims', 5): (401,
{},
{'error': {'code': 'invalid_token',
'message': 'Access token is invalid or expired'}}),
('signed claims', 6): (401,
{},
{'error': {'code': 'invalid_token',
'message': 'Access token is invalid or expired'}}),
('sync validation', 0): (400,
{},
{'error': {'code': 'invalid_change',
'message': 'entity must be transaction, category, or '
'budget'}}),
('sync validation', 1): (400,
{},
{'error': {'code': 'invalid_change',
'message': 'record id is invalid'}}),
('sync validation', 2): (200,
{},
{'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1}],
'cursor': 1,
'hasMore': False}),
('sync validation', 3): (200,
{},
{'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1}],
'cursor': 1,
'hasMore': False}),
('sync validation', 4): (400,
{},
{'error': {'code': 'invalid_change',
'message': 'updatedAt is required'}}),
('sync validation', 5): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('sync validation', 6): (200,
{},
{'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1}],
'cursor': 1,
'hasMore': False}),
('sync validation', 7): (400,
{},
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}}),
('sync validation', 8): (200,
{},
{'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1},
{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': {'amount': 125.5,
'nested': [None, True, 1],
'note': 'Тест €'},
'sequence': 2,
'updatedAt': '2025-01-01T00:00:00.123456789Z',
'version': 2}],
'cursor': 2,
'hasMore': False}),
('sync validation', 9): (200,
{},
{'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1},
{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': {'amount': 125.5,
'nested': [None, True, 1],
'note': 'Тест €'},
'sequence': 2,
'updatedAt': '2025-01-01T00:00:00.123456789Z',
'version': 2}],
'cursor': 2,
'hasMore': False}),
('sync validation', 10): (200,
{},
{'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1},
{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': {'amount': 125.5,
'nested': [None, True, 1],
'note': 'Тест €'},
'sequence': 2,
'updatedAt': '2025-01-01T00:00:00.123456789Z',
'version': 2}],
'cursor': 2,
'hasMore': False}),
('sync validation', 11): (200,
{},
{'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1},
{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': {'amount': 125.5,
'nested': [None, True, 1],
'note': 'Тест €'},
'sequence': 2,
'updatedAt': '2025-01-01T00:00:00.123456789Z',
'version': 2}],
'cursor': 2,
'hasMore': False}),
('sync validation', 12): (400,
{},
{'error': {'code': 'invalid_change',
'message': 'updatedAt is too far in the future'}}),
('sync validation', 13): (400,
{},
{'error': {'code': 'invalid_change',
'message': 'payload is too large'}})},
'raw': {'raw database payloads': [(b'wire-0',
b'{ "duplicate":1, "duplicate":2, "n":123456789012345678901234'
b'567890, "big":1e999 }'),
(b'wire-1',
b'{"lone":"\\ud800","low":"\\udfff","pair":"\\ud83d\\ude00"}'),
(b'wire-2', b'{"invalid":"\xff\xc0\xed\xa0\x80"}'),
(b'wire-3',
b'{"html":"<>&","separator":"\xe2\x80\xa8\xe2\x80\xa9"}'),
(b'wire-4',
b'{"nested":[{"payload":{"id":"inner"}},"quote\\"end",null,true'
b']}'),
(b'\xef\xbf\xbd', b'{}')],
('unicode credentials', 0): (401,
{'error': {'code': 'invalid_credentials',
'message': 'Email or password is incorrect'}}),
('unicode credentials', 1): (401,
{'error': {'code': 'invalid_credentials',
'message': 'Email or password is incorrect'}}),
('unicode id', 0): {'changes': [{'deleted': False,
'entity': 'category',
'id': '�',
'payload': {},
'sequence': 6,
'updatedAt': '2025-01-02T00:00:00Z',
'version': 1}],
'cursor': 6,
'hasMore': False},
('unicode id', 1): {'changes': [], 'cursor': 6, 'hasMore': False},
('wire payload', 0): b'{"duplicate":1,"duplicate":2,"n":123456789012345678901234567890,'
b'"big":1e999}',
('wire payload', 1): b'{"lone":"\\ud800","low":"\\udfff","pair":"\\ud83d\\ude00"}',
('wire payload', 2): b'{"invalid":"\xff\xc0\xed\xa0\x80"}',
('wire payload', 3): b'{"html":"\\u003c\\u003e\\u0026","separator":"\\u2028\\u2029"}',
('wire payload', 4): b'{"nested":[{"payload":{"id":"inner"}},"quote\\"end",null,true]}'},
'scenario': {'/': (200,
{'cache-control': 'no-cache',
'content-security-policy': "default-src 'self'; base-uri 'none'; connect-src "
"'self'; font-src 'self'; form-action 'self'; "
"frame-ancestors 'none'; img-src 'self' data:; "
"manifest-src 'self'; script-src 'self'; style-src "
"'self'; worker-src 'self'",
'content-type': 'text/html; charset=utf-8',
'permissions-policy': 'camera=(), microphone=(), geolocation=(), payment=()',
'referrer-policy': 'no-referrer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'DENY'},
('web asset', 'index.html')),
'/app.js': (200,
{'cache-control': 'public, max-age=3600',
'content-security-policy': "default-src 'self'; base-uri 'none'; "
"connect-src 'self'; font-src 'self'; "
"form-action 'self'; frame-ancestors 'none'; "
"img-src 'self' data:; manifest-src 'self'; "
"script-src 'self'; style-src 'self'; "
"worker-src 'self'",
'content-type': 'text/javascript; charset=utf-8',
'permissions-policy': 'camera=(), microphone=(), geolocation=(), '
'payment=()',
'referrer-policy': 'no-referrer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'DENY'},
('web asset', 'app.js')),
'/icon.svg': (200,
{'cache-control': 'public, max-age=3600',
'content-security-policy': "default-src 'self'; base-uri 'none'; "
"connect-src 'self'; font-src 'self'; "
"form-action 'self'; frame-ancestors "
"'none'; img-src 'self' data:; "
"manifest-src 'self'; script-src 'self'; "
"style-src 'self'; worker-src 'self'",
'content-type': 'image/svg+xml',
'permissions-policy': 'camera=(), microphone=(), geolocation=(), '
'payment=()',
'referrer-policy': 'no-referrer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'DENY'},
('web asset', 'icon.svg')),
'/manifest.webmanifest': (200,
{'cache-control': 'public, max-age=3600',
'content-security-policy': "default-src 'self'; base-uri "
"'none'; connect-src 'self'; "
"font-src 'self'; form-action "
"'self'; frame-ancestors "
"'none'; img-src 'self' data:; "
"manifest-src 'self'; "
"script-src 'self'; style-src "
"'self'; worker-src 'self'",
'content-type': 'text/plain; charset=utf-8',
'permissions-policy': 'camera=(), microphone=(), '
'geolocation=(), payment=()',
'referrer-policy': 'no-referrer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'DENY'},
('web asset', 'manifest.webmanifest')),
'/missing-file': (404,
{'cache-control': None,
'content-security-policy': "default-src 'self'; base-uri 'none'; "
"connect-src 'self'; font-src 'self'; "
"form-action 'self'; frame-ancestors "
"'none'; img-src 'self' data:; "
"manifest-src 'self'; script-src "
"'self'; style-src 'self'; worker-src "
"'self'",
'content-type': 'text/plain; charset=utf-8',
'permissions-policy': 'camera=(), microphone=(), geolocation=(), '
'payment=()',
'referrer-policy': 'no-referrer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'DENY'},
b'404 page not found\n'),
'/service-worker.js': (200,
{'cache-control': 'no-cache',
'content-security-policy': "default-src 'self'; base-uri "
"'none'; connect-src 'self'; "
"font-src 'self'; form-action "
"'self'; frame-ancestors 'none'; "
"img-src 'self' data:; "
"manifest-src 'self'; script-src "
"'self'; style-src 'self'; "
"worker-src 'self'",
'content-type': 'text/javascript; charset=utf-8',
'permissions-policy': 'camera=(), microphone=(), '
'geolocation=(), payment=()',
'referrer-policy': 'no-referrer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'DENY'},
('web asset', 'service-worker.js')),
'/styles.css': (200,
{'cache-control': 'public, max-age=3600',
'content-security-policy': "default-src 'self'; base-uri 'none'; "
"connect-src 'self'; font-src 'self'; "
"form-action 'self'; frame-ancestors "
"'none'; img-src 'self' data:; "
"manifest-src 'self'; script-src 'self'; "
"style-src 'self'; worker-src 'self'",
'content-type': 'text/css; charset=utf-8',
'permissions-policy': 'camera=(), microphone=(), geolocation=(), '
'payment=()',
'referrer-policy': 'no-referrer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'DENY'},
('web asset', 'styles.css')),
'GET /v1/me None': (401,
{'error': {'code': 'authorization_required',
'message': 'Authorization Bearer token is required'}}),
'GET /v1/missing None': (404,
{'error': {'code': 'not_found',
'message': 'Endpoint not found'}}),
"POST /v1/auth/login {'email': 'User@Example.COM', 'password': 'compatibility password'}": (401,
{'error': {'code': 'invalid_credentials',
'message': 'Email '
'or '
'password '
'is '
'incorrect'}}),
"POST /v1/auth/login {'unknown': 1}": (400,
{'error': {'code': 'invalid_json',
'message': 'Request body is not '
'valid JSON'}}),
"POST /v1/auth/refresh {'refreshToken': 'bad'}": (401,
{'error': {'code': 'invalid_refresh_token',
'message': 'Refresh '
'token is '
'invalid or '
'expired'}}),
'POST /v1/auth/refresh {}': (400,
{'error': {'code': 'refresh_token_required',
'message': 'Refresh token is required'}}),
"POST /v1/auth/register {'email': 'a@b', 'password': 'short'}": (400,
{'error': {'code': 'invalid_password',
'message': 'password '
'must '
'contain '
'at '
'least '
'8 '
'characters'}}),
"POST /v1/auth/register {'email': 'bad', 'password': 'compatibility password'}": (400,
{'error': {'code': 'invalid_email',
'message': 'Enter '
'a '
'valid '
'email '
'address'}}),
'deleted': {'changes': [{'deleted': True,
'entity': 'transaction',
'id': 'one',
'sequence': 5,
'updatedAt': '2025-01-02T00:00:00Z',
'version': 2}],
'cursor': 5,
'hasMore': False},
'duplicate': {'error': {'code': 'email_exists',
'message': 'An account with this email already exists'}},
'expired access': {'error': {'code': 'invalid_token',
'message': 'Access token is invalid or expired'}},
'first': {'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1},
{'deleted': False,
'entity': 'transaction',
'id': 'one',
'payload': {'amount': 125.5,
'nested': [None, True, 1],
'note': 'Тест €'},
'sequence': 2,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1},
{'deleted': False,
'entity': 'category',
'id': 'cat',
'payload': {'amount': 125.5,
'nested': [None, True, 1],
'note': 'Тест €'},
'sequence': 3,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1},
{'deleted': False,
'entity': 'budget',
'id': 'budget',
'payload': {'amount': 125.5,
'nested': [None, True, 1],
'note': 'Тест €'},
'sequence': 4,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1}],
'cursor': 4,
'hasMore': False},
'https://allowed.example': (204,
{'access-control-allow-headers': 'Authorization, '
'Content-Type',
'access-control-allow-methods': 'GET, POST, OPTIONS',
'access-control-allow-origin': 'https://allowed.example',
'vary': 'Origin'}),
'https://denied.example': (204, {}),
'idempotent': {'changes': [], 'cursor': 4, 'hasMore': False},
'invalid bearer': {'error': {'code': 'invalid_token',
'message': 'Access token is invalid or expired'}},
"invalid sync {'changes': [{'entity': 'category', 'id': 'x'}]}": (400,
{'error': {'code': 'invalid_change',
'message': 'updatedAt '
'is '
'required'}}),
"invalid sync {'changes': [{'entity': 'transaction', 'id': '', 'updatedAt': '2025-01-01T00:00:00Z', 'payload': {'amount': 125.5, 'note': 'Тест €', 'nested': [None, True, 1]}}]}": (400,
{'error': {'code': 'invalid_change',
'message': 'record '
'id '
'is '
'invalid'}}),
"invalid sync {'changes': [{'entity': 'transaction', 'id': 'x', 'updatedAt': '2025-01-01T00:00:00Z', 'payload': None}]}": (200,
{'changes': [{'deleted': False,
'entity': 'transaction',
'id': 'x',
'payload': None,
'sequence': 1,
'updatedAt': '2025-01-01T00:00:00Z',
'version': 1}],
'cursor': 1,
'hasMore': False}),
"invalid sync {'changes': [{'entity': 'unknown', 'id': 'x', 'updatedAt': '2025-01-01T00:00:00Z', 'payload': {'amount': 125.5, 'note': 'Тест €', 'nested': [None, True, 1]}}]}": (400,
{'error': {'code': 'invalid_change',
'message': 'entity '
'must '
'be '
'transaction, '
'category, '
'or '
'budget'}}),
"invalid sync {'cursor': -1}": (400,
{'error': {'code': 'invalid_sync_request',
'message': 'Cursor or change count is '
'invalid'}}),
"invalid sync {'extra': 1}": (400,
{'error': {'code': 'invalid_json',
'message': 'Request body is not valid '
'JSON'}}),
'logout refresh': {'error': {'code': 'invalid_refresh_token',
'message': 'Refresh token is invalid or expired'}},
'malformed': {'error': {'code': 'invalid_json',
'message': 'Request body is not valid JSON'}},
'pagination': (505, 510),
'rate limit': {'error': {'code': 'rate_limited',
'message': 'Too many authentication requests'}},
'refresh replay': {'error': {'code': 'invalid_refresh_token',
'message': 'Refresh token is invalid or expired'}},
'registration disabled': {'error': {'code': 'registration_disabled',
'message': 'Registration is disabled'}}}}