tangled
alpha
login
or
join now
desertthunder.dev
/
noteleaf
cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists ๐
charm
leaflet
readability
golang
29
fork
atom
overview
issues
2
pulls
pipelines
build: tests for publication commands
desertthunder.dev
3 months ago
c5f8dd26
065b4f82
+615
2 changed files
expand all
collapse all
unified
split
cmd
publication_commands_test.go
internal
services
atproto_test.go
+253
cmd/publication_commands_test.go
···
1
1
+
package main
2
2
+
3
3
+
import (
4
4
+
"context"
5
5
+
"strings"
6
6
+
"testing"
7
7
+
8
8
+
"github.com/stormlightlabs/noteleaf/internal/handlers"
9
9
+
)
10
10
+
11
11
+
func createTestPublicationHandler(t *testing.T) (*handlers.PublicationHandler, func()) {
12
12
+
cleanup := setupCommandTest(t)
13
13
+
handler, err := handlers.NewPublicationHandler()
14
14
+
if err != nil {
15
15
+
cleanup()
16
16
+
t.Fatalf("Failed to create test publication handler: %v", err)
17
17
+
}
18
18
+
return handler, func() {
19
19
+
handler.Close()
20
20
+
cleanup()
21
21
+
}
22
22
+
}
23
23
+
24
24
+
func TestPublicationCommand(t *testing.T) {
25
25
+
t.Run("CommandGroup Interface", func(t *testing.T) {
26
26
+
handler, cleanup := createTestPublicationHandler(t)
27
27
+
defer cleanup()
28
28
+
29
29
+
var _ CommandGroup = NewPublicationCommand(handler)
30
30
+
})
31
31
+
32
32
+
t.Run("Create", func(t *testing.T) {
33
33
+
t.Run("creates command with correct structure", func(t *testing.T) {
34
34
+
handler, cleanup := createTestPublicationHandler(t)
35
35
+
defer cleanup()
36
36
+
37
37
+
cmd := NewPublicationCommand(handler).Create()
38
38
+
39
39
+
if cmd == nil {
40
40
+
t.Fatal("Create returned nil")
41
41
+
}
42
42
+
if cmd.Use != "pub" {
43
43
+
t.Errorf("Expected Use to be 'pub', got '%s'", cmd.Use)
44
44
+
}
45
45
+
if cmd.Short != "Manage leaflet publication sync" {
46
46
+
t.Errorf("Expected Short to be 'Manage leaflet publication sync', got '%s'", cmd.Short)
47
47
+
}
48
48
+
if !cmd.HasSubCommands() {
49
49
+
t.Error("Expected command to have subcommands")
50
50
+
}
51
51
+
})
52
52
+
53
53
+
t.Run("has all expected subcommands", func(t *testing.T) {
54
54
+
handler, cleanup := createTestPublicationHandler(t)
55
55
+
defer cleanup()
56
56
+
57
57
+
cmd := NewPublicationCommand(handler).Create()
58
58
+
subcommands := cmd.Commands()
59
59
+
subcommandNames := make([]string, len(subcommands))
60
60
+
for i, subcmd := range subcommands {
61
61
+
subcommandNames[i] = subcmd.Use
62
62
+
}
63
63
+
64
64
+
expectedSubcommands := []string{
65
65
+
"auth [handle]",
66
66
+
"pull",
67
67
+
"list [--published|--draft|--all]",
68
68
+
"status",
69
69
+
}
70
70
+
71
71
+
for _, expected := range expectedSubcommands {
72
72
+
if !findSubcommand(subcommandNames, expected) {
73
73
+
t.Errorf("Expected subcommand '%s' not found in %v", expected, subcommandNames)
74
74
+
}
75
75
+
}
76
76
+
})
77
77
+
})
78
78
+
79
79
+
t.Run("Status Command", func(t *testing.T) {
80
80
+
t.Run("shows not authenticated initially", func(t *testing.T) {
81
81
+
handler, cleanup := createTestPublicationHandler(t)
82
82
+
defer cleanup()
83
83
+
84
84
+
cmd := NewPublicationCommand(handler).Create()
85
85
+
cmd.SetArgs([]string{"status"})
86
86
+
err := cmd.Execute()
87
87
+
88
88
+
if err != nil {
89
89
+
t.Errorf("status command failed: %v", err)
90
90
+
}
91
91
+
})
92
92
+
})
93
93
+
94
94
+
t.Run("List Command", func(t *testing.T) {
95
95
+
t.Run("default filter", func(t *testing.T) {
96
96
+
handler, cleanup := createTestPublicationHandler(t)
97
97
+
defer cleanup()
98
98
+
99
99
+
cmd := NewPublicationCommand(handler).Create()
100
100
+
cmd.SetArgs([]string{"list"})
101
101
+
err := cmd.Execute()
102
102
+
103
103
+
if err != nil {
104
104
+
t.Errorf("list command failed: %v", err)
105
105
+
}
106
106
+
})
107
107
+
108
108
+
t.Run("with published flag", func(t *testing.T) {
109
109
+
handler, cleanup := createTestPublicationHandler(t)
110
110
+
defer cleanup()
111
111
+
112
112
+
cmd := NewPublicationCommand(handler).Create()
113
113
+
cmd.SetArgs([]string{"list", "--published"})
114
114
+
err := cmd.Execute()
115
115
+
116
116
+
if err != nil {
117
117
+
t.Errorf("list --published failed: %v", err)
118
118
+
}
119
119
+
})
120
120
+
121
121
+
t.Run("with draft flag", func(t *testing.T) {
122
122
+
handler, cleanup := createTestPublicationHandler(t)
123
123
+
defer cleanup()
124
124
+
125
125
+
cmd := NewPublicationCommand(handler).Create()
126
126
+
cmd.SetArgs([]string{"list", "--draft"})
127
127
+
err := cmd.Execute()
128
128
+
129
129
+
if err != nil {
130
130
+
t.Errorf("list --draft failed: %v", err)
131
131
+
}
132
132
+
})
133
133
+
134
134
+
t.Run("with all flag", func(t *testing.T) {
135
135
+
handler, cleanup := createTestPublicationHandler(t)
136
136
+
defer cleanup()
137
137
+
138
138
+
cmd := NewPublicationCommand(handler).Create()
139
139
+
cmd.SetArgs([]string{"list", "--all"})
140
140
+
err := cmd.Execute()
141
141
+
142
142
+
if err != nil {
143
143
+
t.Errorf("list --all failed: %v", err)
144
144
+
}
145
145
+
})
146
146
+
147
147
+
t.Run("published takes precedence over draft", func(t *testing.T) {
148
148
+
handler, cleanup := createTestPublicationHandler(t)
149
149
+
defer cleanup()
150
150
+
151
151
+
cmd := NewPublicationCommand(handler).Create()
152
152
+
cmd.SetArgs([]string{"list", "--published", "--draft"})
153
153
+
err := cmd.Execute()
154
154
+
155
155
+
if err != nil {
156
156
+
t.Errorf("list with multiple flags failed: %v", err)
157
157
+
}
158
158
+
})
159
159
+
})
160
160
+
161
161
+
t.Run("Pull Command", func(t *testing.T) {
162
162
+
t.Run("fails when not authenticated", func(t *testing.T) {
163
163
+
handler, cleanup := createTestPublicationHandler(t)
164
164
+
defer cleanup()
165
165
+
166
166
+
cmd := NewPublicationCommand(handler).Create()
167
167
+
cmd.SetArgs([]string{"pull"})
168
168
+
err := cmd.Execute()
169
169
+
170
170
+
if err == nil {
171
171
+
t.Error("Expected pull to fail when not authenticated")
172
172
+
}
173
173
+
if err != nil && !strings.Contains(err.Error(), "not authenticated") {
174
174
+
t.Errorf("Expected 'not authenticated' error, got: %v", err)
175
175
+
}
176
176
+
})
177
177
+
})
178
178
+
179
179
+
t.Run("Command Help", func(t *testing.T) {
180
180
+
t.Run("root help", func(t *testing.T) {
181
181
+
handler, cleanup := createTestPublicationHandler(t)
182
182
+
defer cleanup()
183
183
+
184
184
+
cmd := NewPublicationCommand(handler).Create()
185
185
+
cmd.SetArgs([]string{"help"})
186
186
+
err := cmd.Execute()
187
187
+
188
188
+
if err != nil {
189
189
+
t.Errorf("help command failed: %v", err)
190
190
+
}
191
191
+
})
192
192
+
193
193
+
t.Run("auth help", func(t *testing.T) {
194
194
+
handler, cleanup := createTestPublicationHandler(t)
195
195
+
defer cleanup()
196
196
+
197
197
+
cmd := NewPublicationCommand(handler).Create()
198
198
+
cmd.SetArgs([]string{"auth", "--help"})
199
199
+
err := cmd.Execute()
200
200
+
201
201
+
if err != nil {
202
202
+
t.Errorf("auth help failed: %v", err)
203
203
+
}
204
204
+
})
205
205
+
})
206
206
+
207
207
+
t.Run("Command Aliases", func(t *testing.T) {
208
208
+
t.Run("list alias ls works", func(t *testing.T) {
209
209
+
handler, cleanup := createTestPublicationHandler(t)
210
210
+
defer cleanup()
211
211
+
212
212
+
cmd := NewPublicationCommand(handler).Create()
213
213
+
cmd.SetArgs([]string{"ls"})
214
214
+
err := cmd.Execute()
215
215
+
216
216
+
if err != nil {
217
217
+
t.Errorf("list alias 'ls' failed: %v", err)
218
218
+
}
219
219
+
})
220
220
+
})
221
221
+
222
222
+
t.Run("Handler Validation", func(t *testing.T) {
223
223
+
t.Run("auth validates empty handle", func(t *testing.T) {
224
224
+
handler, cleanup := createTestPublicationHandler(t)
225
225
+
defer cleanup()
226
226
+
227
227
+
ctx := context.Background()
228
228
+
err := handler.Auth(ctx, "", "password")
229
229
+
230
230
+
if err == nil {
231
231
+
t.Error("Expected error for empty handle")
232
232
+
}
233
233
+
if !strings.Contains(err.Error(), "handle is required") {
234
234
+
t.Errorf("Expected 'handle is required' error, got: %v", err)
235
235
+
}
236
236
+
})
237
237
+
238
238
+
t.Run("auth validates empty password", func(t *testing.T) {
239
239
+
handler, cleanup := createTestPublicationHandler(t)
240
240
+
defer cleanup()
241
241
+
242
242
+
ctx := context.Background()
243
243
+
err := handler.Auth(ctx, "test.bsky.social", "")
244
244
+
245
245
+
if err == nil {
246
246
+
t.Error("Expected error for empty password")
247
247
+
}
248
248
+
if !strings.Contains(err.Error(), "password is required") {
249
249
+
t.Errorf("Expected 'password is required' error, got: %v", err)
250
250
+
}
251
251
+
})
252
252
+
})
253
253
+
}
+362
internal/services/atproto_test.go
···
2
2
3
3
import (
4
4
"context"
5
5
+
"strings"
5
6
"testing"
6
7
"time"
7
8
···
1075
1076
1076
1077
if err != nil && err.Error() == "not authenticated" {
1077
1078
t.Error("Authentication check should pass, but got authentication error")
1079
1079
+
}
1080
1080
+
})
1081
1081
+
})
1082
1082
+
1083
1083
+
t.Run("Session Management Edge Cases", func(t *testing.T) {
1084
1084
+
t.Run("GetSession returns distinct error for nil session", func(t *testing.T) {
1085
1085
+
svc := NewATProtoService()
1086
1086
+
1087
1087
+
session, err := svc.GetSession()
1088
1088
+
if err == nil {
1089
1089
+
t.Error("Expected error when getting nil session")
1090
1090
+
}
1091
1091
+
if session != nil {
1092
1092
+
t.Error("Expected nil session when not authenticated")
1093
1093
+
}
1094
1094
+
expectedMsg := "not authenticated"
1095
1095
+
if !strings.Contains(err.Error(), expectedMsg) {
1096
1096
+
t.Errorf("Expected error message to contain '%s', got '%v'", expectedMsg, err)
1097
1097
+
}
1098
1098
+
})
1099
1099
+
1100
1100
+
t.Run("RestoreSession validates all required fields", func(t *testing.T) {
1101
1101
+
svc := NewATProtoService()
1102
1102
+
1103
1103
+
testCases := []struct {
1104
1104
+
name string
1105
1105
+
session *Session
1106
1106
+
}{
1107
1107
+
{
1108
1108
+
name: "missing DID",
1109
1109
+
session: &Session{
1110
1110
+
DID: "",
1111
1111
+
Handle: "test.bsky.social",
1112
1112
+
AccessJWT: "access",
1113
1113
+
RefreshJWT: "refresh",
1114
1114
+
},
1115
1115
+
},
1116
1116
+
{
1117
1117
+
name: "missing AccessJWT",
1118
1118
+
session: &Session{
1119
1119
+
DID: "did:plc:test",
1120
1120
+
Handle: "test.bsky.social",
1121
1121
+
AccessJWT: "",
1122
1122
+
RefreshJWT: "refresh",
1123
1123
+
},
1124
1124
+
},
1125
1125
+
{
1126
1126
+
name: "missing RefreshJWT",
1127
1127
+
session: &Session{
1128
1128
+
DID: "did:plc:test",
1129
1129
+
Handle: "test.bsky.social",
1130
1130
+
AccessJWT: "access",
1131
1131
+
RefreshJWT: "",
1132
1132
+
},
1133
1133
+
},
1134
1134
+
}
1135
1135
+
1136
1136
+
for _, tc := range testCases {
1137
1137
+
t.Run(tc.name, func(t *testing.T) {
1138
1138
+
err := svc.RestoreSession(tc.session)
1139
1139
+
if err == nil {
1140
1140
+
t.Errorf("Expected error for %s", tc.name)
1141
1141
+
}
1142
1142
+
if !strings.Contains(err.Error(), "session missing required fields") {
1143
1143
+
t.Errorf("Expected 'session missing required fields' error, got: %v", err)
1144
1144
+
}
1145
1145
+
})
1146
1146
+
}
1147
1147
+
})
1148
1148
+
1149
1149
+
t.Run("RestoreSession preserves empty PDSURL", func(t *testing.T) {
1150
1150
+
svc := NewATProtoService()
1151
1151
+
defaultPDSURL := svc.pdsURL
1152
1152
+
1153
1153
+
session := &Session{
1154
1154
+
DID: "did:plc:test123",
1155
1155
+
Handle: "test.bsky.social",
1156
1156
+
AccessJWT: "access_token",
1157
1157
+
RefreshJWT: "refresh_token",
1158
1158
+
PDSURL: "",
1159
1159
+
}
1160
1160
+
1161
1161
+
err := svc.RestoreSession(session)
1162
1162
+
if err != nil {
1163
1163
+
t.Errorf("Expected no error, got %v", err)
1164
1164
+
}
1165
1165
+
1166
1166
+
if svc.pdsURL != defaultPDSURL {
1167
1167
+
t.Errorf("Expected pdsURL to remain default when session PDSURL is empty, got '%s'", svc.pdsURL)
1168
1168
+
}
1169
1169
+
})
1170
1170
+
})
1171
1171
+
1172
1172
+
t.Run("PostDocument Validation", func(t *testing.T) {
1173
1173
+
t.Run("validates title before marshaling", func(t *testing.T) {
1174
1174
+
svc := NewATProtoService()
1175
1175
+
svc.session = &Session{
1176
1176
+
DID: "did:plc:test123",
1177
1177
+
Handle: "test.bsky.social",
1178
1178
+
AccessJWT: "access_token",
1179
1179
+
RefreshJWT: "refresh_token",
1180
1180
+
Authenticated: true,
1181
1181
+
}
1182
1182
+
ctx := context.Background()
1183
1183
+
1184
1184
+
doc := public.Document{
1185
1185
+
Title: "",
1186
1186
+
}
1187
1187
+
1188
1188
+
result, err := svc.PostDocument(ctx, doc, false)
1189
1189
+
if err == nil {
1190
1190
+
t.Error("Expected error when title is empty")
1191
1191
+
}
1192
1192
+
if result != nil {
1193
1193
+
t.Error("Expected nil result when validation fails")
1194
1194
+
}
1195
1195
+
if !strings.Contains(err.Error(), "document title is required") {
1196
1196
+
t.Errorf("Expected 'document title is required' error, got: %v", err)
1197
1197
+
}
1198
1198
+
})
1199
1199
+
1200
1200
+
t.Run("sets correct collection for draft", func(t *testing.T) {
1201
1201
+
svc := NewATProtoService()
1202
1202
+
svc.session = &Session{
1203
1203
+
DID: "did:plc:test123",
1204
1204
+
Handle: "test.bsky.social",
1205
1205
+
AccessJWT: "access_token",
1206
1206
+
RefreshJWT: "refresh_token",
1207
1207
+
Authenticated: true,
1208
1208
+
}
1209
1209
+
ctx := context.Background()
1210
1210
+
1211
1211
+
doc := public.Document{
1212
1212
+
Title: "Test Draft",
1213
1213
+
}
1214
1214
+
1215
1215
+
_, err := svc.PostDocument(ctx, doc, true)
1216
1216
+
1217
1217
+
if err != nil && strings.Contains(err.Error(), "document title is required") {
1218
1218
+
t.Error("Title validation should pass")
1219
1219
+
}
1220
1220
+
})
1221
1221
+
1222
1222
+
t.Run("sets correct collection for published", func(t *testing.T) {
1223
1223
+
svc := NewATProtoService()
1224
1224
+
svc.session = &Session{
1225
1225
+
DID: "did:plc:test123",
1226
1226
+
Handle: "test.bsky.social",
1227
1227
+
AccessJWT: "access_token",
1228
1228
+
RefreshJWT: "refresh_token",
1229
1229
+
Authenticated: true,
1230
1230
+
}
1231
1231
+
ctx := context.Background()
1232
1232
+
1233
1233
+
doc := public.Document{
1234
1234
+
Title: "Test Published",
1235
1235
+
}
1236
1236
+
1237
1237
+
_, err := svc.PostDocument(ctx, doc, false)
1238
1238
+
1239
1239
+
if err != nil && strings.Contains(err.Error(), "document title is required") {
1240
1240
+
t.Error("Title validation should pass")
1241
1241
+
}
1242
1242
+
})
1243
1243
+
})
1244
1244
+
1245
1245
+
t.Run("PatchDocument Validation", func(t *testing.T) {
1246
1246
+
t.Run("validates rkey before title", func(t *testing.T) {
1247
1247
+
svc := NewATProtoService()
1248
1248
+
svc.session = &Session{
1249
1249
+
DID: "did:plc:test123",
1250
1250
+
Handle: "test.bsky.social",
1251
1251
+
AccessJWT: "access_token",
1252
1252
+
RefreshJWT: "refresh_token",
1253
1253
+
Authenticated: true,
1254
1254
+
}
1255
1255
+
ctx := context.Background()
1256
1256
+
1257
1257
+
doc := public.Document{
1258
1258
+
Title: "Valid Title",
1259
1259
+
}
1260
1260
+
1261
1261
+
result, err := svc.PatchDocument(ctx, "", doc, false)
1262
1262
+
if err == nil {
1263
1263
+
t.Error("Expected error when rkey is empty")
1264
1264
+
}
1265
1265
+
if result != nil {
1266
1266
+
t.Error("Expected nil result when rkey validation fails")
1267
1267
+
}
1268
1268
+
if !strings.Contains(err.Error(), "rkey is required") {
1269
1269
+
t.Errorf("Expected 'rkey is required' error, got: %v", err)
1270
1270
+
}
1271
1271
+
})
1272
1272
+
1273
1273
+
t.Run("validates title after rkey", func(t *testing.T) {
1274
1274
+
svc := NewATProtoService()
1275
1275
+
svc.session = &Session{
1276
1276
+
DID: "did:plc:test123",
1277
1277
+
Handle: "test.bsky.social",
1278
1278
+
AccessJWT: "access_token",
1279
1279
+
RefreshJWT: "refresh_token",
1280
1280
+
Authenticated: true,
1281
1281
+
}
1282
1282
+
ctx := context.Background()
1283
1283
+
1284
1284
+
doc := public.Document{
1285
1285
+
Title: "",
1286
1286
+
}
1287
1287
+
1288
1288
+
result, err := svc.PatchDocument(ctx, "valid-rkey", doc, false)
1289
1289
+
if err == nil {
1290
1290
+
t.Error("Expected error when title is empty")
1291
1291
+
}
1292
1292
+
if result != nil {
1293
1293
+
t.Error("Expected nil result when title validation fails")
1294
1294
+
}
1295
1295
+
if !strings.Contains(err.Error(), "document title is required") {
1296
1296
+
t.Errorf("Expected 'document title is required' error, got: %v", err)
1297
1297
+
}
1298
1298
+
})
1299
1299
+
1300
1300
+
t.Run("sets correct collection for draft", func(t *testing.T) {
1301
1301
+
svc := NewATProtoService()
1302
1302
+
svc.session = &Session{
1303
1303
+
DID: "did:plc:test123",
1304
1304
+
Handle: "test.bsky.social",
1305
1305
+
AccessJWT: "access_token",
1306
1306
+
RefreshJWT: "refresh_token",
1307
1307
+
Authenticated: true,
1308
1308
+
}
1309
1309
+
ctx := context.Background()
1310
1310
+
1311
1311
+
doc := public.Document{
1312
1312
+
Title: "Test Draft",
1313
1313
+
}
1314
1314
+
1315
1315
+
_, err := svc.PatchDocument(ctx, "test-rkey", doc, true)
1316
1316
+
1317
1317
+
if err != nil && strings.Contains(err.Error(), "document title is required") {
1318
1318
+
t.Error("Title validation should pass")
1319
1319
+
}
1320
1320
+
})
1321
1321
+
1322
1322
+
t.Run("sets correct collection for published", func(t *testing.T) {
1323
1323
+
svc := NewATProtoService()
1324
1324
+
svc.session = &Session{
1325
1325
+
DID: "did:plc:test123",
1326
1326
+
Handle: "test.bsky.social",
1327
1327
+
AccessJWT: "access_token",
1328
1328
+
RefreshJWT: "refresh_token",
1329
1329
+
Authenticated: true,
1330
1330
+
}
1331
1331
+
ctx := context.Background()
1332
1332
+
1333
1333
+
doc := public.Document{
1334
1334
+
Title: "Test Published",
1335
1335
+
}
1336
1336
+
1337
1337
+
_, err := svc.PatchDocument(ctx, "test-rkey", doc, false)
1338
1338
+
1339
1339
+
if err != nil && strings.Contains(err.Error(), "document title is required") {
1340
1340
+
t.Error("Title validation should pass")
1341
1341
+
}
1342
1342
+
})
1343
1343
+
})
1344
1344
+
1345
1345
+
t.Run("DeleteDocument Validation", func(t *testing.T) {
1346
1346
+
t.Run("validates rkey before attempting delete", func(t *testing.T) {
1347
1347
+
svc := NewATProtoService()
1348
1348
+
svc.session = &Session{
1349
1349
+
DID: "did:plc:test123",
1350
1350
+
Handle: "test.bsky.social",
1351
1351
+
AccessJWT: "access_token",
1352
1352
+
RefreshJWT: "refresh_token",
1353
1353
+
Authenticated: true,
1354
1354
+
}
1355
1355
+
ctx := context.Background()
1356
1356
+
1357
1357
+
err := svc.DeleteDocument(ctx, "", false)
1358
1358
+
if err == nil {
1359
1359
+
t.Error("Expected error when rkey is empty")
1360
1360
+
}
1361
1361
+
if !strings.Contains(err.Error(), "rkey is required") {
1362
1362
+
t.Errorf("Expected 'rkey is required' error, got: %v", err)
1363
1363
+
}
1364
1364
+
})
1365
1365
+
1366
1366
+
t.Run("uses correct collection for draft", func(t *testing.T) {
1367
1367
+
svc := NewATProtoService()
1368
1368
+
svc.session = &Session{
1369
1369
+
DID: "did:plc:test123",
1370
1370
+
Handle: "test.bsky.social",
1371
1371
+
AccessJWT: "access_token",
1372
1372
+
RefreshJWT: "refresh_token",
1373
1373
+
Authenticated: true,
1374
1374
+
}
1375
1375
+
ctx := context.Background()
1376
1376
+
1377
1377
+
err := svc.DeleteDocument(ctx, "test-rkey", true)
1378
1378
+
1379
1379
+
if err != nil && strings.Contains(err.Error(), "rkey is required") {
1380
1380
+
t.Error("Rkey validation should pass")
1381
1381
+
}
1382
1382
+
})
1383
1383
+
1384
1384
+
t.Run("uses correct collection for published", func(t *testing.T) {
1385
1385
+
svc := NewATProtoService()
1386
1386
+
svc.session = &Session{
1387
1387
+
DID: "did:plc:test123",
1388
1388
+
Handle: "test.bsky.social",
1389
1389
+
AccessJWT: "access_token",
1390
1390
+
RefreshJWT: "refresh_token",
1391
1391
+
Authenticated: true,
1392
1392
+
}
1393
1393
+
ctx := context.Background()
1394
1394
+
1395
1395
+
err := svc.DeleteDocument(ctx, "test-rkey", false)
1396
1396
+
1397
1397
+
if err != nil && strings.Contains(err.Error(), "rkey is required") {
1398
1398
+
t.Error("Rkey validation should pass")
1399
1399
+
}
1400
1400
+
})
1401
1401
+
})
1402
1402
+
1403
1403
+
t.Run("Concurrent Operations", func(t *testing.T) {
1404
1404
+
t.Run("Close can be called multiple times", func(t *testing.T) {
1405
1405
+
svc := NewATProtoService()
1406
1406
+
svc.session = &Session{
1407
1407
+
Handle: "test.bsky.social",
1408
1408
+
Authenticated: true,
1409
1409
+
}
1410
1410
+
1411
1411
+
err1 := svc.Close()
1412
1412
+
if err1 != nil {
1413
1413
+
t.Errorf("First close should succeed: %v", err1)
1414
1414
+
}
1415
1415
+
1416
1416
+
err2 := svc.Close()
1417
1417
+
if err2 != nil {
1418
1418
+
t.Errorf("Second close should succeed: %v", err2)
1419
1419
+
}
1420
1420
+
})
1421
1421
+
1422
1422
+
t.Run("IsAuthenticated after Close returns false", func(t *testing.T) {
1423
1423
+
svc := NewATProtoService()
1424
1424
+
svc.session = &Session{
1425
1425
+
Handle: "test.bsky.social",
1426
1426
+
Authenticated: true,
1427
1427
+
}
1428
1428
+
1429
1429
+
if !svc.IsAuthenticated() {
1430
1430
+
t.Error("Expected IsAuthenticated to return true before close")
1431
1431
+
}
1432
1432
+
1433
1433
+
err := svc.Close()
1434
1434
+
if err != nil {
1435
1435
+
t.Errorf("Close failed: %v", err)
1436
1436
+
}
1437
1437
+
1438
1438
+
if svc.IsAuthenticated() {
1439
1439
+
t.Error("Expected IsAuthenticated to return false after close")
1078
1440
}
1079
1441
})
1080
1442
})