Skip to content

Commit e0ba3b2

Browse files
Merge pull request #21 from Chinchill-AI/fixtures-and-postgres-fix
feat: TS webhook fixtures + parity tests + Postgres lock fix
2 parents fcfe55b + 4fac8f6 commit e0ba3b2

34 files changed

Lines changed: 6383 additions & 0 deletions

tests/fixtures/__init__.py

Whitespace-only changes.

tests/fixtures/conftest.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Fixture helpers for loading replay test fixtures.
2+
3+
Fixtures are JSON files from the TS Chat SDK integration tests.
4+
They live in tests/fixtures/replay/ (copied from the TS repo).
5+
"""
6+
7+
from __future__ import annotations
8+
9+
import json
10+
from pathlib import Path
11+
12+
FIXTURE_DIR = Path(__file__).parent / "replay"
13+
14+
# Fallback to TS repo path if fixtures haven't been copied yet
15+
_TS_FIXTURE_DIR = Path("/tmp/vercel-chat/packages/integration-tests/fixtures/replay")
16+
17+
18+
def load_fixture(relative_path: str) -> dict:
19+
"""Load a JSON fixture file by relative path (e.g., 'slack.json')."""
20+
local_path = FIXTURE_DIR / relative_path
21+
if local_path.exists():
22+
return json.loads(local_path.read_text())
23+
24+
ts_path = _TS_FIXTURE_DIR / relative_path
25+
if ts_path.exists():
26+
return json.loads(ts_path.read_text())
27+
28+
raise FileNotFoundError(
29+
f"Fixture not found: {relative_path}\n"
30+
f" Looked in: {FIXTURE_DIR}\n"
31+
f" Fallback: {_TS_FIXTURE_DIR}\n"
32+
f" Run: python tests/fixtures/copy_fixtures.py"
33+
)

tests/fixtures/copy_fixtures.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""One-time script to copy fixture files from TS repo.
2+
3+
Run: python tests/fixtures/copy_fixtures.py
4+
"""
5+
6+
import json
7+
import os
8+
import shutil
9+
import sys
10+
11+
SRC = "/tmp/vercel-chat/packages/integration-tests/fixtures/replay"
12+
DST = os.path.join(os.path.dirname(__file__), "replay")
13+
14+
15+
def main():
16+
if not os.path.isdir(SRC):
17+
print(f"Source not found: {SRC}", file=sys.stderr)
18+
sys.exit(1)
19+
20+
count = 0
21+
for root, _dirs, files in os.walk(SRC):
22+
for fname in sorted(files):
23+
if not fname.endswith(".json"):
24+
continue
25+
rel = os.path.relpath(os.path.join(root, fname), SRC)
26+
dst_path = os.path.join(DST, rel)
27+
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
28+
shutil.copy2(os.path.join(root, fname), dst_path)
29+
count += 1
30+
print(f" Copied: {rel}")
31+
32+
print(f"\nCopied {count} fixture files to {DST}")
33+
34+
35+
if __name__ == "__main__":
36+
main()
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
{
2+
"botName": "Chat SDK Demo",
3+
"botUserId": "users/100000000000000000002",
4+
"mention": {
5+
"commonEventObject": {
6+
"userLocale": "en",
7+
"hostApp": "CHAT",
8+
"platform": "WEB"
9+
},
10+
"chat": {
11+
"user": {
12+
"name": "users/100000000000000000001",
13+
"displayName": "Test User",
14+
"type": "HUMAN"
15+
},
16+
"eventTime": "2026-01-02T23:32:28.751807Z",
17+
"messagePayload": {
18+
"space": {
19+
"name": "spaces/AAQAO1heGsE",
20+
"type": "ROOM",
21+
"displayName": "Test Chat SDK 4",
22+
"spaceType": "SPACE"
23+
},
24+
"message": {
25+
"name": "spaces/AAQAO1heGsE/messages/A6woZAHrIjs.A6woZAHrIjs",
26+
"sender": {
27+
"name": "users/100000000000000000001",
28+
"displayName": "Test User",
29+
"type": "HUMAN"
30+
},
31+
"createTime": "2026-01-02T23:32:28.751807Z",
32+
"text": "@Chat SDK Demo Hey",
33+
"annotations": [
34+
{
35+
"type": "USER_MENTION",
36+
"startIndex": 0,
37+
"length": 14,
38+
"userMention": {
39+
"user": {
40+
"name": "users/100000000000000000002",
41+
"displayName": "Chat SDK Demo",
42+
"type": "BOT"
43+
},
44+
"type": "MENTION"
45+
}
46+
}
47+
],
48+
"thread": { "name": "spaces/AAQAO1heGsE/threads/A6woZAHrIjs" },
49+
"space": { "name": "spaces/AAQAO1heGsE" }
50+
}
51+
}
52+
}
53+
},
54+
"action": {
55+
"commonEventObject": {
56+
"userLocale": "en",
57+
"hostApp": "CHAT",
58+
"platform": "WEB",
59+
"parameters": {
60+
"actionId": "hello"
61+
}
62+
},
63+
"chat": {
64+
"user": {
65+
"name": "users/100000000000000000001",
66+
"displayName": "Test User",
67+
"type": "HUMAN"
68+
},
69+
"eventTime": "2026-01-02T23:32:34.175996Z",
70+
"buttonClickedPayload": {
71+
"space": {
72+
"name": "spaces/AAQAO1heGsE",
73+
"type": "ROOM",
74+
"displayName": "Test Chat SDK 4",
75+
"spaceType": "SPACE"
76+
},
77+
"message": {
78+
"name": "spaces/AAQAO1heGsE/messages/A6woZAHrIjs.9qQCF6JGUNg",
79+
"sender": {
80+
"name": "users/100000000000000000002",
81+
"displayName": "Chat SDK Demo",
82+
"type": "BOT"
83+
},
84+
"createTime": "2026-01-02T23:32:31.646079Z",
85+
"thread": { "name": "spaces/AAQAO1heGsE/threads/A6woZAHrIjs" },
86+
"space": { "name": "spaces/AAQAO1heGsE" }
87+
}
88+
}
89+
}
90+
},
91+
"reaction": {
92+
"message": {
93+
"attributes": {
94+
"ce-datacontenttype": "application/json",
95+
"ce-id": "spaces/AAQADmenPpY/spaceEvents/MTc2NzMyNjEwMzIzODkyNF81OF9jcmVhdGVk",
96+
"ce-source": "//workspaceevents.googleapis.com/subscriptions/chat-spaces-czpBQVFBRG1lblBwWToxMTc5OTQ4NzMzNTQzNzU4NjAwODk6MTEzOTc3OTE2MjAxNTUyMzQ2MTQ2",
97+
"ce-specversion": "1.0",
98+
"ce-subject": "//chat.googleapis.com/spaces/AAQADmenPpY",
99+
"ce-time": "2026-01-02T03:55:03.238924Z",
100+
"ce-type": "google.workspace.chat.reaction.v1.created"
101+
},
102+
"data": "eyJyZWFjdGlvbiI6eyJuYW1lIjoic3BhY2VzL0FBUUFEbWVuUHBZL21lc3NhZ2VzL2hDQ3lET2hhNy1vLjRsUXZNdUVHTTBnL3JlYWN0aW9ucy8xMDAwMDAwMDAwMDAwMDAwMDAwMDEuVlU1SlEwOUVSUzd3bjVHTiIsInVzZXIiOnsibmFtZSI6InVzZXJzLzEwMDAwMDAwMDAwMDAwMDAwMDAwMSIsInR5cGUiOiJIVU1BTiJ9LCJlbW9qaSI6eyJ1bmljb2RlIjoi8J+RjSJ9fX0="
103+
},
104+
"subscription": "projects/example-chat-project-123456/subscriptions/chat-messages-push"
105+
}
106+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
{
2+
"botName": "Chat SDK ExampleBot",
3+
"botUserId": "U00FAKEBOT01",
4+
"mention": {
5+
"token": "xAbCdEfGhIjKlMnOpQrStUvW",
6+
"team_id": "T00FAKE00AA",
7+
"api_app_id": "A00FAKEAPP01",
8+
"event": {
9+
"type": "app_mention",
10+
"user": "U00FAKEUSER1",
11+
"ts": "1767326125.870439",
12+
"text": "<@U00FAKEBOT01> Test",
13+
"channel": "C00FAKECHAN1",
14+
"event_ts": "1767326125.870439"
15+
},
16+
"type": "event_callback",
17+
"authorizations": [{ "user_id": "U00FAKEBOT01", "is_bot": true }]
18+
},
19+
"action": {
20+
"type": "block_actions",
21+
"user": {
22+
"id": "U00FAKEUSER1",
23+
"username": "testuser",
24+
"name": "testuser",
25+
"team_id": "T00FAKE00AA"
26+
},
27+
"api_app_id": "A00FAKEAPP01",
28+
"token": "xAbCdEfGhIjKlMnOpQrStUvW",
29+
"container": {
30+
"type": "message",
31+
"message_ts": "1767326126.896109",
32+
"channel_id": "C00FAKECHAN1",
33+
"is_ephemeral": false,
34+
"thread_ts": "1767326125.870439"
35+
},
36+
"trigger_id": "10215325802133.3901254001572.2e2548aa918b35fd85829545c2d4ae2b",
37+
"team": {
38+
"id": "T00FAKE00AA",
39+
"domain": "vercelslacktesting"
40+
},
41+
"channel": {
42+
"id": "C00FAKECHAN1",
43+
"name": "chat-sdk"
44+
},
45+
"message": {
46+
"user": "U00FAKEBOT01",
47+
"type": "message",
48+
"ts": "1767326126.896109",
49+
"thread_ts": "1767326125.870439"
50+
},
51+
"actions": [
52+
{
53+
"type": "button",
54+
"block_id": "P0z+f",
55+
"action_id": "info",
56+
"text": {
57+
"type": "plain_text",
58+
"text": "Show Info",
59+
"emoji": true
60+
},
61+
"action_ts": "1767326130.289415"
62+
}
63+
],
64+
"response_url": "https://hooks.slack.com/actions/T00FAKE00AA/10231680918177/hMWwGmzsWEYbB4BjtNump6kO"
65+
},
66+
"reaction": {
67+
"token": "xAbCdEfGhIjKlMnOpQrStUvW",
68+
"team_id": "T00FAKE00AA",
69+
"api_app_id": "A00FAKEAPP01",
70+
"event": {
71+
"type": "reaction_added",
72+
"user": "U00FAKEUSER1",
73+
"reaction": "+1",
74+
"item": {
75+
"type": "message",
76+
"channel": "C00FAKECHAN1",
77+
"ts": "1767326126.896109"
78+
},
79+
"item_user": "U00FAKEBOT01",
80+
"event_ts": "1767326140.000700"
81+
},
82+
"type": "event_callback",
83+
"authorizations": [{ "user_id": "U00FAKEBOT01", "is_bot": true }]
84+
},
85+
"staticSelectAction": {
86+
"type": "block_actions",
87+
"user": {
88+
"id": "U00FAKEUSER1",
89+
"username": "testuser",
90+
"name": "testuser",
91+
"team_id": "T00FAKE00AA"
92+
},
93+
"api_app_id": "A00FAKEAPP01",
94+
"token": "xAbCdEfGhIjKlMnOpQrStUvW",
95+
"container": {
96+
"type": "message",
97+
"message_ts": "1767326126.896109",
98+
"channel_id": "C00FAKECHAN1",
99+
"is_ephemeral": false,
100+
"thread_ts": "1767326125.870439"
101+
},
102+
"trigger_id": "10215325802133.3901254001572.staticselect123",
103+
"team": {
104+
"id": "T00FAKE00AA",
105+
"domain": "vercelslacktesting"
106+
},
107+
"channel": {
108+
"id": "C00FAKECHAN1",
109+
"name": "chat-sdk"
110+
},
111+
"message": {
112+
"user": "U00FAKEBOT01",
113+
"type": "message",
114+
"ts": "1767326126.896109",
115+
"thread_ts": "1767326125.870439"
116+
},
117+
"actions": [
118+
{
119+
"type": "static_select",
120+
"action_id": "quick_action",
121+
"block_id": "ikh4M",
122+
"selected_option": {
123+
"text": {
124+
"type": "plain_text",
125+
"text": "Say Hello",
126+
"emoji": true
127+
},
128+
"value": "greet"
129+
},
130+
"placeholder": {
131+
"type": "plain_text",
132+
"text": "Choose...",
133+
"emoji": true
134+
},
135+
"action_ts": "1767326130.289415"
136+
}
137+
],
138+
"response_url": "https://hooks.slack.com/actions/T00FAKE00AA/10231680918177/staticSelectExample"
139+
},
140+
"radioButtonsAction": {
141+
"type": "block_actions",
142+
"user": {
143+
"id": "U00FAKEUSER1",
144+
"username": "testuser",
145+
"name": "testuser",
146+
"team_id": "T00FAKE00AA"
147+
},
148+
"api_app_id": "A00FAKEAPP01",
149+
"token": "xAbCdEfGhIjKlMnOpQrStUvW",
150+
"container": {
151+
"type": "message",
152+
"message_ts": "1767326126.896109",
153+
"channel_id": "C00FAKECHAN1",
154+
"is_ephemeral": false,
155+
"thread_ts": "1767326125.870439"
156+
},
157+
"trigger_id": "10215325802133.3901254001572.radiobuttons123",
158+
"team": {
159+
"id": "T00FAKE00AA",
160+
"domain": "vercelslacktesting"
161+
},
162+
"channel": {
163+
"id": "C00FAKECHAN1",
164+
"name": "chat-sdk"
165+
},
166+
"message": {
167+
"user": "U00FAKEBOT01",
168+
"type": "message",
169+
"ts": "1767326126.896109",
170+
"thread_ts": "1767326125.870439"
171+
},
172+
"actions": [
173+
{
174+
"type": "radio_buttons",
175+
"action_id": "plan_selected",
176+
"block_id": "yCgjj",
177+
"selected_option": {
178+
"text": {
179+
"type": "mrkdwn",
180+
"text": "*All text elements*",
181+
"verbatim": false
182+
},
183+
"value": "all_text",
184+
"description": {
185+
"type": "mrkdwn",
186+
"text": "Headers, body text, labels, and placeholders",
187+
"verbatim": false
188+
}
189+
},
190+
"action_ts": "1767326135.289415"
191+
}
192+
],
193+
"response_url": "https://hooks.slack.com/actions/T00FAKE00AA/10231680918177/radioButtonsExample"
194+
}
195+
}

0 commit comments

Comments
 (0)