Coverage for src/tests/test_events.py: 100%

1159 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-12-02 20:27 +0000

1from datetime import timedelta 

2 

3import grpc 

4import pytest 

5from google.protobuf import wrappers_pb2 

6from psycopg2.extras import DateTimeTZRange 

7from sqlalchemy.sql.expression import update 

8 

9from couchers import errors 

10from couchers.db import session_scope 

11from couchers.models import EventOccurrence 

12from couchers.tasks import enforce_community_memberships 

13from couchers.utils import Timestamp_from_datetime, now, to_aware_datetime 

14from proto import admin_pb2, events_pb2, threads_pb2 

15from tests.test_communities import create_community, create_group 

16from tests.test_fixtures import ( # noqa 

17 db, 

18 email_fields, 

19 events_session, 

20 generate_user, 

21 mock_notification_email, 

22 real_admin_session, 

23 testconfig, 

24 threads_session, 

25) 

26 

27 

28@pytest.fixture(autouse=True) 

29def _(testconfig): 

30 pass 

31 

32 

33def test_CreateEvent(db): 

34 # test cases: 

35 # can create event 

36 # cannot create event with missing details 

37 # can create online event 

38 # can create in person event 

39 # can't create event that starts in the past 

40 # can create in different timezones 

41 

42 # event creator 

43 user1, token1 = generate_user() 

44 # community moderator 

45 user2, token2 = generate_user() 

46 # third party 

47 user3, token3 = generate_user() 

48 

49 with session_scope() as session: 

50 c_id = create_community(session, 0, 2, "Community", [user2], [], None).id 

51 

52 time_before = now() 

53 start_time = now() + timedelta(hours=2) 

54 end_time = start_time + timedelta(hours=3) 

55 

56 with events_session(token1) as api: 

57 # in person event 

58 res = api.CreateEvent( 

59 events_pb2.CreateEventReq( 

60 title="Dummy Title", 

61 content="Dummy content.", 

62 photo_key=None, 

63 offline_information=events_pb2.OfflineEventInformation( 

64 address="Near Null Island", 

65 lat=0.1, 

66 lng=0.2, 

67 ), 

68 start_time=Timestamp_from_datetime(start_time), 

69 end_time=Timestamp_from_datetime(end_time), 

70 timezone="UTC", 

71 ) 

72 ) 

73 

74 assert res.is_next 

75 assert res.title == "Dummy Title" 

76 assert res.slug == "dummy-title" 

77 assert res.content == "Dummy content." 

78 assert not res.photo_url 

79 assert res.WhichOneof("mode") == "offline_information" 

80 assert res.offline_information.lat == 0.1 

81 assert res.offline_information.lng == 0.2 

82 assert res.offline_information.address == "Near Null Island" 

83 assert time_before <= to_aware_datetime(res.created) <= now() 

84 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

85 assert res.creator_user_id == user1.id 

86 assert to_aware_datetime(res.start_time) == start_time 

87 assert to_aware_datetime(res.end_time) == end_time 

88 # assert res.timezone == "UTC" 

89 assert res.start_time_display 

90 assert res.end_time_display 

91 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

92 assert res.organizer 

93 assert res.subscriber 

94 assert res.going_count == 1 

95 assert res.maybe_count == 0 

96 assert res.organizer_count == 1 

97 assert res.subscriber_count == 1 

98 assert res.owner_user_id == user1.id 

99 assert not res.owner_community_id 

100 assert not res.owner_group_id 

101 assert res.thread.thread_id 

102 assert res.can_edit 

103 assert not res.can_moderate 

104 

105 event_id = res.event_id 

106 

107 with events_session(token2) as api: 

108 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

109 

110 assert res.is_next 

111 assert res.title == "Dummy Title" 

112 assert res.slug == "dummy-title" 

113 assert res.content == "Dummy content." 

114 assert not res.photo_url 

115 assert res.WhichOneof("mode") == "offline_information" 

116 assert res.offline_information.lat == 0.1 

117 assert res.offline_information.lng == 0.2 

118 assert res.offline_information.address == "Near Null Island" 

119 assert time_before <= to_aware_datetime(res.created) <= now() 

120 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

121 assert res.creator_user_id == user1.id 

122 assert to_aware_datetime(res.start_time) == start_time 

123 assert to_aware_datetime(res.end_time) == end_time 

124 # assert res.timezone == "UTC" 

125 assert res.start_time_display 

126 assert res.end_time_display 

127 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

128 assert not res.organizer 

129 assert not res.subscriber 

130 assert res.going_count == 1 

131 assert res.maybe_count == 0 

132 assert res.organizer_count == 1 

133 assert res.subscriber_count == 1 

134 assert res.owner_user_id == user1.id 

135 assert not res.owner_community_id 

136 assert not res.owner_group_id 

137 assert res.thread.thread_id 

138 assert res.can_edit 

139 assert res.can_moderate 

140 

141 with events_session(token3) as api: 

142 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

143 

144 assert res.is_next 

145 assert res.title == "Dummy Title" 

146 assert res.slug == "dummy-title" 

147 assert res.content == "Dummy content." 

148 assert not res.photo_url 

149 assert res.WhichOneof("mode") == "offline_information" 

150 assert res.offline_information.lat == 0.1 

151 assert res.offline_information.lng == 0.2 

152 assert res.offline_information.address == "Near Null Island" 

153 assert time_before <= to_aware_datetime(res.created) <= now() 

154 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

155 assert res.creator_user_id == user1.id 

156 assert to_aware_datetime(res.start_time) == start_time 

157 assert to_aware_datetime(res.end_time) == end_time 

158 # assert res.timezone == "UTC" 

159 assert res.start_time_display 

160 assert res.end_time_display 

161 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

162 assert not res.organizer 

163 assert not res.subscriber 

164 assert res.going_count == 1 

165 assert res.maybe_count == 0 

166 assert res.organizer_count == 1 

167 assert res.subscriber_count == 1 

168 assert res.owner_user_id == user1.id 

169 assert not res.owner_community_id 

170 assert not res.owner_group_id 

171 assert res.thread.thread_id 

172 assert not res.can_edit 

173 assert not res.can_moderate 

174 

175 with events_session(token1) as api: 

176 # online only event 

177 res = api.CreateEvent( 

178 events_pb2.CreateEventReq( 

179 title="Dummy Title", 

180 content="Dummy content.", 

181 photo_key=None, 

182 online_information=events_pb2.OnlineEventInformation( 

183 link="https://couchers.org/meet/", 

184 ), 

185 parent_community_id=c_id, 

186 start_time=Timestamp_from_datetime(start_time), 

187 end_time=Timestamp_from_datetime(end_time), 

188 timezone="UTC", 

189 ) 

190 ) 

191 

192 assert res.is_next 

193 assert res.title == "Dummy Title" 

194 assert res.slug == "dummy-title" 

195 assert res.content == "Dummy content." 

196 assert not res.photo_url 

197 assert res.WhichOneof("mode") == "online_information" 

198 assert res.online_information.link == "https://couchers.org/meet/" 

199 assert time_before <= to_aware_datetime(res.created) <= now() 

200 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

201 assert res.creator_user_id == user1.id 

202 assert to_aware_datetime(res.start_time) == start_time 

203 assert to_aware_datetime(res.end_time) == end_time 

204 # assert res.timezone == "UTC" 

205 assert res.start_time_display 

206 assert res.end_time_display 

207 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

208 assert res.organizer 

209 assert res.subscriber 

210 assert res.going_count == 1 

211 assert res.maybe_count == 0 

212 assert res.organizer_count == 1 

213 assert res.subscriber_count == 1 

214 assert res.owner_user_id == user1.id 

215 assert not res.owner_community_id 

216 assert not res.owner_group_id 

217 assert res.thread.thread_id 

218 assert res.can_edit 

219 assert not res.can_moderate 

220 

221 event_id = res.event_id 

222 

223 with events_session(token2) as api: 

224 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

225 

226 assert res.is_next 

227 assert res.title == "Dummy Title" 

228 assert res.slug == "dummy-title" 

229 assert res.content == "Dummy content." 

230 assert not res.photo_url 

231 assert res.WhichOneof("mode") == "online_information" 

232 assert res.online_information.link == "https://couchers.org/meet/" 

233 assert time_before <= to_aware_datetime(res.created) <= now() 

234 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

235 assert res.creator_user_id == user1.id 

236 assert to_aware_datetime(res.start_time) == start_time 

237 assert to_aware_datetime(res.end_time) == end_time 

238 # assert res.timezone == "UTC" 

239 assert res.start_time_display 

240 assert res.end_time_display 

241 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

242 assert not res.organizer 

243 assert not res.subscriber 

244 assert res.going_count == 1 

245 assert res.maybe_count == 0 

246 assert res.organizer_count == 1 

247 assert res.subscriber_count == 1 

248 assert res.owner_user_id == user1.id 

249 assert not res.owner_community_id 

250 assert not res.owner_group_id 

251 assert res.thread.thread_id 

252 assert res.can_edit 

253 assert res.can_moderate 

254 

255 with events_session(token3) as api: 

256 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

257 

258 assert res.is_next 

259 assert res.title == "Dummy Title" 

260 assert res.slug == "dummy-title" 

261 assert res.content == "Dummy content." 

262 assert not res.photo_url 

263 assert res.WhichOneof("mode") == "online_information" 

264 assert res.online_information.link == "https://couchers.org/meet/" 

265 assert time_before <= to_aware_datetime(res.created) <= now() 

266 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

267 assert res.creator_user_id == user1.id 

268 assert to_aware_datetime(res.start_time) == start_time 

269 assert to_aware_datetime(res.end_time) == end_time 

270 # assert res.timezone == "UTC" 

271 assert res.start_time_display 

272 assert res.end_time_display 

273 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

274 assert not res.organizer 

275 assert not res.subscriber 

276 assert res.going_count == 1 

277 assert res.maybe_count == 0 

278 assert res.organizer_count == 1 

279 assert res.subscriber_count == 1 

280 assert res.owner_user_id == user1.id 

281 assert not res.owner_community_id 

282 assert not res.owner_group_id 

283 assert res.thread.thread_id 

284 assert not res.can_edit 

285 assert not res.can_moderate 

286 

287 with events_session(token1) as api: 

288 with pytest.raises(grpc.RpcError) as e: 

289 api.CreateEvent( 

290 events_pb2.CreateEventReq( 

291 title="Dummy Title", 

292 content="Dummy content.", 

293 photo_key=None, 

294 online_information=events_pb2.OnlineEventInformation( 

295 link="https://couchers.org/meet/", 

296 ), 

297 start_time=Timestamp_from_datetime(start_time), 

298 end_time=Timestamp_from_datetime(end_time), 

299 timezone="UTC", 

300 ) 

301 ) 

302 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

303 assert e.value.details() == errors.ONLINE_EVENT_MISSING_PARENT_COMMUNITY 

304 

305 with pytest.raises(grpc.RpcError) as e: 

306 api.CreateEvent( 

307 events_pb2.CreateEventReq( 

308 # title="Dummy Title", 

309 content="Dummy content.", 

310 photo_key=None, 

311 offline_information=events_pb2.OfflineEventInformation( 

312 address="Near Null Island", 

313 lat=0.1, 

314 lng=0.1, 

315 ), 

316 start_time=Timestamp_from_datetime(start_time), 

317 end_time=Timestamp_from_datetime(end_time), 

318 timezone="UTC", 

319 ) 

320 ) 

321 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

322 assert e.value.details() == errors.MISSING_EVENT_TITLE 

323 

324 with pytest.raises(grpc.RpcError) as e: 

325 api.CreateEvent( 

326 events_pb2.CreateEventReq( 

327 title="Dummy Title", 

328 # content="Dummy content.", 

329 photo_key=None, 

330 offline_information=events_pb2.OfflineEventInformation( 

331 address="Near Null Island", 

332 lat=0.1, 

333 lng=0.1, 

334 ), 

335 start_time=Timestamp_from_datetime(start_time), 

336 end_time=Timestamp_from_datetime(end_time), 

337 timezone="UTC", 

338 ) 

339 ) 

340 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

341 assert e.value.details() == errors.MISSING_EVENT_CONTENT 

342 

343 with pytest.raises(grpc.RpcError) as e: 

344 api.CreateEvent( 

345 events_pb2.CreateEventReq( 

346 title="Dummy Title", 

347 content="Dummy content.", 

348 photo_key="nonexistent", 

349 offline_information=events_pb2.OfflineEventInformation( 

350 address="Near Null Island", 

351 lat=0.1, 

352 lng=0.1, 

353 ), 

354 start_time=Timestamp_from_datetime(start_time), 

355 end_time=Timestamp_from_datetime(end_time), 

356 timezone="UTC", 

357 ) 

358 ) 

359 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

360 assert e.value.details() == errors.PHOTO_NOT_FOUND 

361 

362 with pytest.raises(grpc.RpcError) as e: 

363 api.CreateEvent( 

364 events_pb2.CreateEventReq( 

365 title="Dummy Title", 

366 content="Dummy content.", 

367 photo_key=None, 

368 offline_information=events_pb2.OfflineEventInformation( 

369 address="Near Null Island", 

370 ), 

371 start_time=Timestamp_from_datetime(start_time), 

372 end_time=Timestamp_from_datetime(end_time), 

373 timezone="UTC", 

374 ) 

375 ) 

376 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

377 assert e.value.details() == errors.MISSING_EVENT_ADDRESS_OR_LOCATION 

378 

379 with pytest.raises(grpc.RpcError) as e: 

380 api.CreateEvent( 

381 events_pb2.CreateEventReq( 

382 title="Dummy Title", 

383 content="Dummy content.", 

384 photo_key=None, 

385 offline_information=events_pb2.OfflineEventInformation( 

386 lat=0.1, 

387 lng=0.1, 

388 ), 

389 start_time=Timestamp_from_datetime(start_time), 

390 end_time=Timestamp_from_datetime(end_time), 

391 timezone="UTC", 

392 ) 

393 ) 

394 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

395 assert e.value.details() == errors.MISSING_EVENT_ADDRESS_OR_LOCATION 

396 

397 with pytest.raises(grpc.RpcError) as e: 

398 api.CreateEvent( 

399 events_pb2.CreateEventReq( 

400 title="Dummy Title", 

401 content="Dummy content.", 

402 photo_key=None, 

403 online_information=events_pb2.OnlineEventInformation(), 

404 start_time=Timestamp_from_datetime(start_time), 

405 end_time=Timestamp_from_datetime(end_time), 

406 timezone="UTC", 

407 ) 

408 ) 

409 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

410 assert e.value.details() == errors.ONLINE_EVENT_REQUIRES_LINK 

411 

412 with pytest.raises(grpc.RpcError) as e: 

413 api.CreateEvent( 

414 events_pb2.CreateEventReq( 

415 title="Dummy Title", 

416 content="Dummy content.", 

417 parent_community_id=c_id, 

418 online_information=events_pb2.OnlineEventInformation( 

419 link="https://couchers.org/meet/", 

420 ), 

421 start_time=Timestamp_from_datetime(now() - timedelta(hours=2)), 

422 end_time=Timestamp_from_datetime(end_time), 

423 timezone="UTC", 

424 ) 

425 ) 

426 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

427 assert e.value.details() == errors.EVENT_IN_PAST 

428 

429 with pytest.raises(grpc.RpcError) as e: 

430 api.CreateEvent( 

431 events_pb2.CreateEventReq( 

432 title="Dummy Title", 

433 content="Dummy content.", 

434 parent_community_id=c_id, 

435 online_information=events_pb2.OnlineEventInformation( 

436 link="https://couchers.org/meet/", 

437 ), 

438 start_time=Timestamp_from_datetime(end_time), 

439 end_time=Timestamp_from_datetime(start_time), 

440 timezone="UTC", 

441 ) 

442 ) 

443 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

444 assert e.value.details() == errors.EVENT_ENDS_BEFORE_STARTS 

445 

446 with pytest.raises(grpc.RpcError) as e: 

447 api.CreateEvent( 

448 events_pb2.CreateEventReq( 

449 title="Dummy Title", 

450 content="Dummy content.", 

451 parent_community_id=c_id, 

452 online_information=events_pb2.OnlineEventInformation( 

453 link="https://couchers.org/meet/", 

454 ), 

455 start_time=Timestamp_from_datetime(now() + timedelta(days=500, hours=2)), 

456 end_time=Timestamp_from_datetime(now() + timedelta(days=500, hours=5)), 

457 timezone="UTC", 

458 ) 

459 ) 

460 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

461 assert e.value.details() == errors.EVENT_TOO_FAR_IN_FUTURE 

462 

463 with pytest.raises(grpc.RpcError) as e: 

464 api.CreateEvent( 

465 events_pb2.CreateEventReq( 

466 title="Dummy Title", 

467 content="Dummy content.", 

468 parent_community_id=c_id, 

469 online_information=events_pb2.OnlineEventInformation( 

470 link="https://couchers.org/meet/", 

471 ), 

472 start_time=Timestamp_from_datetime(start_time), 

473 end_time=Timestamp_from_datetime(now() + timedelta(days=100)), 

474 timezone="UTC", 

475 ) 

476 ) 

477 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

478 assert e.value.details() == errors.EVENT_TOO_LONG 

479 

480 

481def test_CreateEvent_incomplete_profile(db): 

482 user1, token1 = generate_user(complete_profile=False) 

483 user2, token2 = generate_user() 

484 

485 with session_scope() as session: 

486 c_id = create_community(session, 0, 2, "Community", [user2], [], None).id 

487 

488 start_time = now() + timedelta(hours=2) 

489 end_time = start_time + timedelta(hours=3) 

490 

491 with events_session(token1) as api: 

492 with pytest.raises(grpc.RpcError) as e: 

493 api.CreateEvent( 

494 events_pb2.CreateEventReq( 

495 title="Dummy Title", 

496 content="Dummy content.", 

497 photo_key=None, 

498 offline_information=events_pb2.OfflineEventInformation( 

499 address="Near Null Island", 

500 lat=0.1, 

501 lng=0.2, 

502 ), 

503 start_time=Timestamp_from_datetime(start_time), 

504 end_time=Timestamp_from_datetime(end_time), 

505 timezone="UTC", 

506 ) 

507 ) 

508 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

509 assert e.value.details() == errors.INCOMPLETE_PROFILE_CREATE_EVENT 

510 

511 

512def test_ScheduleEvent(db): 

513 # test cases: 

514 # can schedule a new event occurrence 

515 

516 user, token = generate_user() 

517 

518 with session_scope() as session: 

519 c_id = create_community(session, 0, 2, "Community", [user], [], None).id 

520 

521 time_before = now() 

522 start_time = now() + timedelta(hours=2) 

523 end_time = start_time + timedelta(hours=3) 

524 

525 with events_session(token) as api: 

526 res = api.CreateEvent( 

527 events_pb2.CreateEventReq( 

528 title="Dummy Title", 

529 content="Dummy content.", 

530 parent_community_id=c_id, 

531 online_information=events_pb2.OnlineEventInformation( 

532 link="https://couchers.org/meet/", 

533 ), 

534 start_time=Timestamp_from_datetime(start_time), 

535 end_time=Timestamp_from_datetime(end_time), 

536 timezone="UTC", 

537 ) 

538 ) 

539 

540 new_start_time = now() + timedelta(hours=6) 

541 new_end_time = new_start_time + timedelta(hours=2) 

542 

543 res = api.ScheduleEvent( 

544 events_pb2.ScheduleEventReq( 

545 event_id=res.event_id, 

546 content="New event occurrence", 

547 offline_information=events_pb2.OfflineEventInformation( 

548 address="A bit further but still near Null Island", 

549 lat=0.3, 

550 lng=0.2, 

551 ), 

552 start_time=Timestamp_from_datetime(new_start_time), 

553 end_time=Timestamp_from_datetime(new_end_time), 

554 timezone="UTC", 

555 ) 

556 ) 

557 

558 res = api.GetEvent(events_pb2.GetEventReq(event_id=res.event_id)) 

559 

560 assert not res.is_next 

561 assert res.title == "Dummy Title" 

562 assert res.slug == "dummy-title" 

563 assert res.content == "New event occurrence" 

564 assert not res.photo_url 

565 assert res.WhichOneof("mode") == "offline_information" 

566 assert res.offline_information.lat == 0.3 

567 assert res.offline_information.lng == 0.2 

568 assert res.offline_information.address == "A bit further but still near Null Island" 

569 assert time_before <= to_aware_datetime(res.created) <= now() 

570 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

571 assert res.creator_user_id == user.id 

572 assert to_aware_datetime(res.start_time) == new_start_time 

573 assert to_aware_datetime(res.end_time) == new_end_time 

574 # assert res.timezone == "UTC" 

575 assert res.start_time_display 

576 assert res.end_time_display 

577 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

578 assert res.organizer 

579 assert res.subscriber 

580 assert res.going_count == 1 

581 assert res.maybe_count == 0 

582 assert res.organizer_count == 1 

583 assert res.subscriber_count == 1 

584 assert res.owner_user_id == user.id 

585 assert not res.owner_community_id 

586 assert not res.owner_group_id 

587 assert res.thread.thread_id 

588 assert res.can_edit 

589 assert res.can_moderate 

590 

591 

592def test_cannot_overlap_occurrences_schedule(db): 

593 user, token = generate_user() 

594 

595 with session_scope() as session: 

596 c_id = create_community(session, 0, 2, "Community", [user], [], None).id 

597 

598 start = now() 

599 

600 with events_session(token) as api: 

601 res = api.CreateEvent( 

602 events_pb2.CreateEventReq( 

603 title="Dummy Title", 

604 content="Dummy content.", 

605 parent_community_id=c_id, 

606 online_information=events_pb2.OnlineEventInformation( 

607 link="https://couchers.org/meet/", 

608 ), 

609 start_time=Timestamp_from_datetime(start + timedelta(hours=1)), 

610 end_time=Timestamp_from_datetime(start + timedelta(hours=3)), 

611 timezone="UTC", 

612 ) 

613 ) 

614 

615 with pytest.raises(grpc.RpcError) as e: 

616 api.ScheduleEvent( 

617 events_pb2.ScheduleEventReq( 

618 event_id=res.event_id, 

619 content="New event occurrence", 

620 offline_information=events_pb2.OfflineEventInformation( 

621 address="A bit further but still near Null Island", 

622 lat=0.3, 

623 lng=0.2, 

624 ), 

625 start_time=Timestamp_from_datetime(start + timedelta(hours=2)), 

626 end_time=Timestamp_from_datetime(start + timedelta(hours=6)), 

627 timezone="UTC", 

628 ) 

629 ) 

630 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

631 assert e.value.details() == errors.EVENT_CANT_OVERLAP 

632 

633 

634def test_cannot_overlap_occurrences_update(db): 

635 user, token = generate_user() 

636 

637 with session_scope() as session: 

638 c_id = create_community(session, 0, 2, "Community", [user], [], None).id 

639 

640 start = now() 

641 

642 with events_session(token) as api: 

643 res = api.CreateEvent( 

644 events_pb2.CreateEventReq( 

645 title="Dummy Title", 

646 content="Dummy content.", 

647 parent_community_id=c_id, 

648 online_information=events_pb2.OnlineEventInformation( 

649 link="https://couchers.org/meet/", 

650 ), 

651 start_time=Timestamp_from_datetime(start + timedelta(hours=1)), 

652 end_time=Timestamp_from_datetime(start + timedelta(hours=3)), 

653 timezone="UTC", 

654 ) 

655 ) 

656 

657 event_id = api.ScheduleEvent( 

658 events_pb2.ScheduleEventReq( 

659 event_id=res.event_id, 

660 content="New event occurrence", 

661 offline_information=events_pb2.OfflineEventInformation( 

662 address="A bit further but still near Null Island", 

663 lat=0.3, 

664 lng=0.2, 

665 ), 

666 start_time=Timestamp_from_datetime(start + timedelta(hours=4)), 

667 end_time=Timestamp_from_datetime(start + timedelta(hours=6)), 

668 timezone="UTC", 

669 ) 

670 ).event_id 

671 

672 # can overlap with this current existing occurrence 

673 api.UpdateEvent( 

674 events_pb2.UpdateEventReq( 

675 event_id=event_id, 

676 start_time=Timestamp_from_datetime(start + timedelta(hours=5)), 

677 end_time=Timestamp_from_datetime(start + timedelta(hours=6)), 

678 ) 

679 ) 

680 

681 with pytest.raises(grpc.RpcError) as e: 

682 api.UpdateEvent( 

683 events_pb2.UpdateEventReq( 

684 event_id=event_id, 

685 start_time=Timestamp_from_datetime(start + timedelta(hours=2)), 

686 end_time=Timestamp_from_datetime(start + timedelta(hours=4)), 

687 ) 

688 ) 

689 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

690 assert e.value.details() == errors.EVENT_CANT_OVERLAP 

691 

692 

693def test_UpdateEvent_single(db): 

694 # test cases: 

695 # owner can update 

696 # community owner can update 

697 # can't mess up online/in person dichotomy 

698 # notifies attendees 

699 

700 # event creator 

701 user1, token1 = generate_user() 

702 # community moderator 

703 user2, token2 = generate_user() 

704 # third parties 

705 user3, token3 = generate_user() 

706 user4, token4 = generate_user() 

707 user5, token5 = generate_user() 

708 user6, token6 = generate_user() 

709 

710 with session_scope() as session: 

711 c_id = create_community(session, 0, 2, "Community", [user2], [], None).id 

712 

713 time_before = now() 

714 start_time = now() + timedelta(hours=2) 

715 end_time = start_time + timedelta(hours=3) 

716 

717 with events_session(token1) as api: 

718 res = api.CreateEvent( 

719 events_pb2.CreateEventReq( 

720 title="Dummy Title", 

721 content="Dummy content.", 

722 offline_information=events_pb2.OfflineEventInformation( 

723 address="Near Null Island", 

724 lat=0.1, 

725 lng=0.2, 

726 ), 

727 start_time=Timestamp_from_datetime(start_time), 

728 end_time=Timestamp_from_datetime(end_time), 

729 timezone="UTC", 

730 ) 

731 ) 

732 

733 event_id = res.event_id 

734 

735 with events_session(token4) as api: 

736 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

737 

738 with events_session(token5) as api: 

739 api.SetEventAttendance( 

740 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

741 ) 

742 

743 with events_session(token6) as api: 

744 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

745 api.SetEventAttendance( 

746 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_MAYBE) 

747 ) 

748 

749 time_before_update = now() 

750 

751 with events_session(token1) as api: 

752 res = api.UpdateEvent( 

753 events_pb2.UpdateEventReq( 

754 event_id=event_id, 

755 ) 

756 ) 

757 

758 with events_session(token1) as api: 

759 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

760 

761 assert res.is_next 

762 assert res.title == "Dummy Title" 

763 assert res.slug == "dummy-title" 

764 assert res.content == "Dummy content." 

765 assert not res.photo_url 

766 assert res.WhichOneof("mode") == "offline_information" 

767 assert res.offline_information.lat == 0.1 

768 assert res.offline_information.lng == 0.2 

769 assert res.offline_information.address == "Near Null Island" 

770 assert time_before <= to_aware_datetime(res.created) <= time_before_update 

771 assert time_before_update <= to_aware_datetime(res.last_edited) <= now() 

772 assert res.creator_user_id == user1.id 

773 assert to_aware_datetime(res.start_time) == start_time 

774 assert to_aware_datetime(res.end_time) == end_time 

775 # assert res.timezone == "UTC" 

776 assert res.start_time_display 

777 assert res.end_time_display 

778 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

779 assert res.organizer 

780 assert res.subscriber 

781 assert res.going_count == 2 

782 assert res.maybe_count == 1 

783 assert res.organizer_count == 1 

784 assert res.subscriber_count == 3 

785 assert res.owner_user_id == user1.id 

786 assert not res.owner_community_id 

787 assert not res.owner_group_id 

788 assert res.thread.thread_id 

789 assert res.can_edit 

790 assert not res.can_moderate 

791 

792 with events_session(token2) as api: 

793 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

794 

795 assert res.is_next 

796 assert res.title == "Dummy Title" 

797 assert res.slug == "dummy-title" 

798 assert res.content == "Dummy content." 

799 assert not res.photo_url 

800 assert res.WhichOneof("mode") == "offline_information" 

801 assert res.offline_information.lat == 0.1 

802 assert res.offline_information.lng == 0.2 

803 assert res.offline_information.address == "Near Null Island" 

804 assert time_before <= to_aware_datetime(res.created) <= time_before_update 

805 assert time_before_update <= to_aware_datetime(res.last_edited) <= now() 

806 assert res.creator_user_id == user1.id 

807 assert to_aware_datetime(res.start_time) == start_time 

808 assert to_aware_datetime(res.end_time) == end_time 

809 # assert res.timezone == "UTC" 

810 assert res.start_time_display 

811 assert res.end_time_display 

812 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

813 assert not res.organizer 

814 assert not res.subscriber 

815 assert res.going_count == 2 

816 assert res.maybe_count == 1 

817 assert res.organizer_count == 1 

818 assert res.subscriber_count == 3 

819 assert res.owner_user_id == user1.id 

820 assert not res.owner_community_id 

821 assert not res.owner_group_id 

822 assert res.thread.thread_id 

823 assert res.can_edit 

824 assert res.can_moderate 

825 

826 with events_session(token3) as api: 

827 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

828 

829 assert res.is_next 

830 assert res.title == "Dummy Title" 

831 assert res.slug == "dummy-title" 

832 assert res.content == "Dummy content." 

833 assert not res.photo_url 

834 assert res.WhichOneof("mode") == "offline_information" 

835 assert res.offline_information.lat == 0.1 

836 assert res.offline_information.lng == 0.2 

837 assert res.offline_information.address == "Near Null Island" 

838 assert time_before <= to_aware_datetime(res.created) <= time_before_update 

839 assert time_before_update <= to_aware_datetime(res.last_edited) <= now() 

840 assert res.creator_user_id == user1.id 

841 assert to_aware_datetime(res.start_time) == start_time 

842 assert to_aware_datetime(res.end_time) == end_time 

843 # assert res.timezone == "UTC" 

844 assert res.start_time_display 

845 assert res.end_time_display 

846 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

847 assert not res.organizer 

848 assert not res.subscriber 

849 assert res.going_count == 2 

850 assert res.maybe_count == 1 

851 assert res.organizer_count == 1 

852 assert res.subscriber_count == 3 

853 assert res.owner_user_id == user1.id 

854 assert not res.owner_community_id 

855 assert not res.owner_group_id 

856 assert res.thread.thread_id 

857 assert not res.can_edit 

858 assert not res.can_moderate 

859 

860 with events_session(token1) as api: 

861 res = api.UpdateEvent( 

862 events_pb2.UpdateEventReq( 

863 event_id=event_id, 

864 title=wrappers_pb2.StringValue(value="Dummy Title"), 

865 content=wrappers_pb2.StringValue(value="Dummy content."), 

866 online_information=events_pb2.OnlineEventInformation(link="https://couchers.org/meet/"), 

867 start_time=Timestamp_from_datetime(start_time), 

868 end_time=Timestamp_from_datetime(end_time), 

869 timezone=wrappers_pb2.StringValue(value="UTC"), 

870 ) 

871 ) 

872 

873 assert res.is_next 

874 assert res.title == "Dummy Title" 

875 assert res.slug == "dummy-title" 

876 assert res.content == "Dummy content." 

877 assert not res.photo_url 

878 assert res.WhichOneof("mode") == "online_information" 

879 assert res.online_information.link == "https://couchers.org/meet/" 

880 assert time_before <= to_aware_datetime(res.created) <= time_before_update 

881 assert time_before_update <= to_aware_datetime(res.last_edited) <= now() 

882 assert res.creator_user_id == user1.id 

883 assert to_aware_datetime(res.start_time) == start_time 

884 assert to_aware_datetime(res.end_time) == end_time 

885 # assert res.timezone == "UTC" 

886 assert res.start_time_display 

887 assert res.end_time_display 

888 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

889 assert res.organizer 

890 assert res.subscriber 

891 assert res.going_count == 2 

892 assert res.maybe_count == 1 

893 assert res.organizer_count == 1 

894 assert res.subscriber_count == 3 

895 assert res.owner_user_id == user1.id 

896 assert not res.owner_community_id 

897 assert not res.owner_group_id 

898 assert res.thread.thread_id 

899 assert res.can_edit 

900 assert not res.can_moderate 

901 

902 event_id = res.event_id 

903 

904 with events_session(token2) as api: 

905 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

906 

907 assert res.is_next 

908 assert res.title == "Dummy Title" 

909 assert res.slug == "dummy-title" 

910 assert res.content == "Dummy content." 

911 assert not res.photo_url 

912 assert res.WhichOneof("mode") == "online_information" 

913 assert res.online_information.link == "https://couchers.org/meet/" 

914 assert time_before <= to_aware_datetime(res.created) <= time_before_update 

915 assert time_before_update <= to_aware_datetime(res.last_edited) <= now() 

916 assert res.creator_user_id == user1.id 

917 assert to_aware_datetime(res.start_time) == start_time 

918 assert to_aware_datetime(res.end_time) == end_time 

919 # assert res.timezone == "UTC" 

920 assert res.start_time_display 

921 assert res.end_time_display 

922 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

923 assert not res.organizer 

924 assert not res.subscriber 

925 assert res.going_count == 2 

926 assert res.maybe_count == 1 

927 assert res.organizer_count == 1 

928 assert res.subscriber_count == 3 

929 assert res.owner_user_id == user1.id 

930 assert not res.owner_community_id 

931 assert not res.owner_group_id 

932 assert res.thread.thread_id 

933 assert res.can_edit 

934 assert res.can_moderate 

935 

936 with events_session(token3) as api: 

937 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

938 

939 assert res.is_next 

940 assert res.title == "Dummy Title" 

941 assert res.slug == "dummy-title" 

942 assert res.content == "Dummy content." 

943 assert not res.photo_url 

944 assert res.WhichOneof("mode") == "online_information" 

945 assert res.online_information.link == "https://couchers.org/meet/" 

946 assert time_before <= to_aware_datetime(res.created) <= time_before_update 

947 assert time_before_update <= to_aware_datetime(res.last_edited) <= now() 

948 assert res.creator_user_id == user1.id 

949 assert to_aware_datetime(res.start_time) == start_time 

950 assert to_aware_datetime(res.end_time) == end_time 

951 # assert res.timezone == "UTC" 

952 assert res.start_time_display 

953 assert res.end_time_display 

954 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

955 assert not res.organizer 

956 assert not res.subscriber 

957 assert res.going_count == 2 

958 assert res.maybe_count == 1 

959 assert res.organizer_count == 1 

960 assert res.subscriber_count == 3 

961 assert res.owner_user_id == user1.id 

962 assert not res.owner_community_id 

963 assert not res.owner_group_id 

964 assert res.thread.thread_id 

965 assert not res.can_edit 

966 assert not res.can_moderate 

967 

968 with events_session(token1) as api: 

969 res = api.UpdateEvent( 

970 events_pb2.UpdateEventReq( 

971 event_id=event_id, 

972 offline_information=events_pb2.OfflineEventInformation( 

973 address="Near Null Island", 

974 lat=0.1, 

975 lng=0.2, 

976 ), 

977 ) 

978 ) 

979 

980 with events_session(token3) as api: 

981 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

982 

983 assert res.WhichOneof("mode") == "offline_information" 

984 assert res.offline_information.address == "Near Null Island" 

985 assert res.offline_information.lat == 0.1 

986 assert res.offline_information.lng == 0.2 

987 

988 

989def test_UpdateEvent_all(db): 

990 # event creator 

991 user1, token1 = generate_user() 

992 # community moderator 

993 user2, token2 = generate_user() 

994 # third parties 

995 user3, token3 = generate_user() 

996 user4, token4 = generate_user() 

997 user5, token5 = generate_user() 

998 user6, token6 = generate_user() 

999 

1000 with session_scope() as session: 

1001 c_id = create_community(session, 0, 2, "Community", [user2], [], None).id 

1002 

1003 time_before = now() 

1004 start_time = now() + timedelta(hours=1) 

1005 end_time = start_time + timedelta(hours=1.5) 

1006 

1007 event_ids = [] 

1008 

1009 with events_session(token1) as api: 

1010 res = api.CreateEvent( 

1011 events_pb2.CreateEventReq( 

1012 title="Dummy Title", 

1013 content="0th occurrence", 

1014 offline_information=events_pb2.OfflineEventInformation( 

1015 address="Near Null Island", 

1016 lat=0.1, 

1017 lng=0.2, 

1018 ), 

1019 start_time=Timestamp_from_datetime(start_time), 

1020 end_time=Timestamp_from_datetime(end_time), 

1021 timezone="UTC", 

1022 ) 

1023 ) 

1024 

1025 event_id = res.event_id 

1026 event_ids.append(event_id) 

1027 

1028 with events_session(token4) as api: 

1029 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1030 

1031 with events_session(token5) as api: 

1032 api.SetEventAttendance( 

1033 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

1034 ) 

1035 

1036 with events_session(token6) as api: 

1037 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1038 api.SetEventAttendance( 

1039 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_MAYBE) 

1040 ) 

1041 

1042 with events_session(token1) as api: 

1043 for i in range(5): 

1044 res = api.ScheduleEvent( 

1045 events_pb2.ScheduleEventReq( 

1046 event_id=event_ids[-1], 

1047 content=f"{i+1}th occurrence", 

1048 online_information=events_pb2.OnlineEventInformation( 

1049 link="https://couchers.org/meet/", 

1050 ), 

1051 start_time=Timestamp_from_datetime(start_time + timedelta(hours=2 + i)), 

1052 end_time=Timestamp_from_datetime(start_time + timedelta(hours=2.5 + i)), 

1053 timezone="UTC", 

1054 ) 

1055 ) 

1056 

1057 event_ids.append(res.event_id) 

1058 

1059 updated_event_id = event_ids[3] 

1060 

1061 time_before_update = now() 

1062 

1063 with events_session(token1) as api: 

1064 res = api.UpdateEvent( 

1065 events_pb2.UpdateEventReq( 

1066 event_id=updated_event_id, 

1067 title=wrappers_pb2.StringValue(value="New Title"), 

1068 content=wrappers_pb2.StringValue(value="New content."), 

1069 online_information=events_pb2.OnlineEventInformation(link="https://couchers.org/meet/"), 

1070 update_all_future=True, 

1071 ) 

1072 ) 

1073 

1074 time_after_update = now() 

1075 

1076 with events_session(token2) as api: 

1077 for i in range(3): 

1078 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_ids[i])) 

1079 assert res.content == f"{i}th occurrence" 

1080 assert time_before <= to_aware_datetime(res.last_edited) <= time_before_update 

1081 

1082 for i in range(3, 6): 

1083 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_ids[i])) 

1084 assert res.content == "New content." 

1085 assert time_before_update <= to_aware_datetime(res.last_edited) <= time_after_update 

1086 

1087 

1088def test_GetEvent(db): 

1089 # event creator 

1090 user1, token1 = generate_user() 

1091 # community moderator 

1092 user2, token2 = generate_user() 

1093 # third parties 

1094 user3, token3 = generate_user() 

1095 user4, token4 = generate_user() 

1096 user5, token5 = generate_user() 

1097 user6, token6 = generate_user() 

1098 

1099 with session_scope() as session: 

1100 c_id = create_community(session, 0, 2, "Community", [user2], [], None).id 

1101 

1102 time_before = now() 

1103 start_time = now() + timedelta(hours=2) 

1104 end_time = start_time + timedelta(hours=3) 

1105 

1106 with events_session(token1) as api: 

1107 # in person event 

1108 res = api.CreateEvent( 

1109 events_pb2.CreateEventReq( 

1110 title="Dummy Title", 

1111 content="Dummy content.", 

1112 offline_information=events_pb2.OfflineEventInformation( 

1113 address="Near Null Island", 

1114 lat=0.1, 

1115 lng=0.2, 

1116 ), 

1117 start_time=Timestamp_from_datetime(start_time), 

1118 end_time=Timestamp_from_datetime(end_time), 

1119 timezone="UTC", 

1120 ) 

1121 ) 

1122 

1123 event_id = res.event_id 

1124 

1125 with events_session(token4) as api: 

1126 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1127 

1128 with events_session(token5) as api: 

1129 api.SetEventAttendance( 

1130 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

1131 ) 

1132 

1133 with events_session(token6) as api: 

1134 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1135 api.SetEventAttendance( 

1136 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_MAYBE) 

1137 ) 

1138 

1139 with events_session(token1) as api: 

1140 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

1141 

1142 assert res.is_next 

1143 assert res.title == "Dummy Title" 

1144 assert res.slug == "dummy-title" 

1145 assert res.content == "Dummy content." 

1146 assert not res.photo_url 

1147 assert res.WhichOneof("mode") == "offline_information" 

1148 assert res.offline_information.lat == 0.1 

1149 assert res.offline_information.lng == 0.2 

1150 assert res.offline_information.address == "Near Null Island" 

1151 assert time_before <= to_aware_datetime(res.created) <= now() 

1152 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

1153 assert res.creator_user_id == user1.id 

1154 assert to_aware_datetime(res.start_time) == start_time 

1155 assert to_aware_datetime(res.end_time) == end_time 

1156 # assert res.timezone == "UTC" 

1157 assert res.start_time_display 

1158 assert res.end_time_display 

1159 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

1160 assert res.organizer 

1161 assert res.subscriber 

1162 assert res.going_count == 2 

1163 assert res.maybe_count == 1 

1164 assert res.organizer_count == 1 

1165 assert res.subscriber_count == 3 

1166 assert res.owner_user_id == user1.id 

1167 assert not res.owner_community_id 

1168 assert not res.owner_group_id 

1169 assert res.thread.thread_id 

1170 assert res.can_edit 

1171 assert not res.can_moderate 

1172 

1173 with events_session(token2) as api: 

1174 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

1175 

1176 assert res.is_next 

1177 assert res.title == "Dummy Title" 

1178 assert res.slug == "dummy-title" 

1179 assert res.content == "Dummy content." 

1180 assert not res.photo_url 

1181 assert res.WhichOneof("mode") == "offline_information" 

1182 assert res.offline_information.lat == 0.1 

1183 assert res.offline_information.lng == 0.2 

1184 assert res.offline_information.address == "Near Null Island" 

1185 assert time_before <= to_aware_datetime(res.created) <= now() 

1186 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

1187 assert res.creator_user_id == user1.id 

1188 assert to_aware_datetime(res.start_time) == start_time 

1189 assert to_aware_datetime(res.end_time) == end_time 

1190 # assert res.timezone == "UTC" 

1191 assert res.start_time_display 

1192 assert res.end_time_display 

1193 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1194 assert not res.organizer 

1195 assert not res.subscriber 

1196 assert res.going_count == 2 

1197 assert res.maybe_count == 1 

1198 assert res.organizer_count == 1 

1199 assert res.subscriber_count == 3 

1200 assert res.owner_user_id == user1.id 

1201 assert not res.owner_community_id 

1202 assert not res.owner_group_id 

1203 assert res.thread.thread_id 

1204 assert res.can_edit 

1205 assert res.can_moderate 

1206 

1207 with events_session(token3) as api: 

1208 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

1209 

1210 assert res.is_next 

1211 assert res.title == "Dummy Title" 

1212 assert res.slug == "dummy-title" 

1213 assert res.content == "Dummy content." 

1214 assert not res.photo_url 

1215 assert res.WhichOneof("mode") == "offline_information" 

1216 assert res.offline_information.lat == 0.1 

1217 assert res.offline_information.lng == 0.2 

1218 assert res.offline_information.address == "Near Null Island" 

1219 assert time_before <= to_aware_datetime(res.created) <= now() 

1220 assert time_before <= to_aware_datetime(res.last_edited) <= now() 

1221 assert res.creator_user_id == user1.id 

1222 assert to_aware_datetime(res.start_time) == start_time 

1223 assert to_aware_datetime(res.end_time) == end_time 

1224 # assert res.timezone == "UTC" 

1225 assert res.start_time_display 

1226 assert res.end_time_display 

1227 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1228 assert not res.organizer 

1229 assert not res.subscriber 

1230 assert res.going_count == 2 

1231 assert res.maybe_count == 1 

1232 assert res.organizer_count == 1 

1233 assert res.subscriber_count == 3 

1234 assert res.owner_user_id == user1.id 

1235 assert not res.owner_community_id 

1236 assert not res.owner_group_id 

1237 assert res.thread.thread_id 

1238 assert not res.can_edit 

1239 assert not res.can_moderate 

1240 

1241 

1242def test_CancelEvent(db): 

1243 # event creator 

1244 user1, token1 = generate_user() 

1245 # community moderator 

1246 user2, token2 = generate_user() 

1247 # third parties 

1248 user3, token3 = generate_user() 

1249 user4, token4 = generate_user() 

1250 user5, token5 = generate_user() 

1251 user6, token6 = generate_user() 

1252 

1253 with session_scope() as session: 

1254 c_id = create_community(session, 0, 2, "Community", [user2], [], None).id 

1255 

1256 start_time = now() + timedelta(hours=2) 

1257 end_time = start_time + timedelta(hours=3) 

1258 

1259 with events_session(token1) as api: 

1260 res = api.CreateEvent( 

1261 events_pb2.CreateEventReq( 

1262 title="Dummy Title", 

1263 content="Dummy content.", 

1264 offline_information=events_pb2.OfflineEventInformation( 

1265 address="Near Null Island", 

1266 lat=0.1, 

1267 lng=0.2, 

1268 ), 

1269 start_time=Timestamp_from_datetime(start_time), 

1270 end_time=Timestamp_from_datetime(end_time), 

1271 timezone="UTC", 

1272 ) 

1273 ) 

1274 

1275 event_id = res.event_id 

1276 

1277 with events_session(token4) as api: 

1278 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1279 

1280 with events_session(token5) as api: 

1281 api.SetEventAttendance( 

1282 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

1283 ) 

1284 

1285 with events_session(token6) as api: 

1286 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1287 api.SetEventAttendance( 

1288 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_MAYBE) 

1289 ) 

1290 

1291 with events_session(token1) as api: 

1292 res = api.CancelEvent( 

1293 events_pb2.CancelEventReq( 

1294 event_id=event_id, 

1295 ) 

1296 ) 

1297 

1298 with events_session(token1) as api: 

1299 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

1300 assert res.is_cancelled 

1301 

1302 with events_session(token1) as api: 

1303 with pytest.raises(grpc.RpcError) as e: 

1304 api.UpdateEvent( 

1305 events_pb2.UpdateEventReq( 

1306 event_id=event_id, 

1307 title=wrappers_pb2.StringValue(value="New Title"), 

1308 ) 

1309 ) 

1310 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1311 assert e.value.details() == errors.EVENT_CANT_UPDATE_CANCELLED_EVENT 

1312 

1313 with pytest.raises(grpc.RpcError) as e: 

1314 api.InviteEventOrganizer( 

1315 events_pb2.InviteEventOrganizerReq( 

1316 event_id=event_id, 

1317 user_id=user3.id, 

1318 ) 

1319 ) 

1320 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1321 assert e.value.details() == errors.EVENT_CANT_UPDATE_CANCELLED_EVENT 

1322 

1323 with pytest.raises(grpc.RpcError) as e: 

1324 api.TransferEvent(events_pb2.TransferEventReq(event_id=event_id, new_owner_community_id=c_id)) 

1325 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1326 assert e.value.details() == errors.EVENT_CANT_UPDATE_CANCELLED_EVENT 

1327 

1328 with events_session(token3) as api: 

1329 with pytest.raises(grpc.RpcError) as e: 

1330 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1331 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1332 assert e.value.details() == errors.EVENT_CANT_UPDATE_CANCELLED_EVENT 

1333 

1334 with pytest.raises(grpc.RpcError) as e: 

1335 api.SetEventAttendance( 

1336 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

1337 ) 

1338 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1339 assert e.value.details() == errors.EVENT_CANT_UPDATE_CANCELLED_EVENT 

1340 

1341 with events_session(token1) as api: 

1342 for include_cancelled in [True, False]: 

1343 res = api.ListEventOccurrences( 

1344 events_pb2.ListEventOccurrencesReq( 

1345 event_id=event_id, 

1346 include_cancelled=include_cancelled, 

1347 ) 

1348 ) 

1349 if include_cancelled: 

1350 assert len(res.events) > 0 

1351 else: 

1352 assert len(res.events) == 0 

1353 

1354 res = api.ListMyEvents( 

1355 events_pb2.ListMyEventsReq( 

1356 include_cancelled=include_cancelled, 

1357 ) 

1358 ) 

1359 if include_cancelled: 

1360 assert len(res.events) > 0 

1361 else: 

1362 assert len(res.events) == 0 

1363 

1364 

1365def test_ListEventAttendees(db): 

1366 # event creator 

1367 user1, token1 = generate_user() 

1368 # others 

1369 user2, token2 = generate_user() 

1370 user3, token3 = generate_user() 

1371 user4, token4 = generate_user() 

1372 user5, token5 = generate_user() 

1373 user6, token6 = generate_user() 

1374 

1375 with session_scope() as session: 

1376 c_id = create_community(session, 0, 2, "Community", [user1], [], None).id 

1377 

1378 with events_session(token1) as api: 

1379 event_id = api.CreateEvent( 

1380 events_pb2.CreateEventReq( 

1381 title="Dummy Title", 

1382 content="Dummy content.", 

1383 offline_information=events_pb2.OfflineEventInformation( 

1384 address="Near Null Island", 

1385 lat=0.1, 

1386 lng=0.2, 

1387 ), 

1388 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1389 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1390 timezone="UTC", 

1391 ) 

1392 ).event_id 

1393 

1394 for token in [token2, token3, token4, token5]: 

1395 with events_session(token) as api: 

1396 api.SetEventAttendance( 

1397 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

1398 ) 

1399 

1400 with events_session(token6) as api: 

1401 assert api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).going_count == 5 

1402 

1403 res = api.ListEventAttendees(events_pb2.ListEventAttendeesReq(event_id=event_id, page_size=2)) 

1404 assert res.attendee_user_ids == [user1.id, user2.id] 

1405 

1406 res = api.ListEventAttendees( 

1407 events_pb2.ListEventAttendeesReq(event_id=event_id, page_size=2, page_token=res.next_page_token) 

1408 ) 

1409 assert res.attendee_user_ids == [user3.id, user4.id] 

1410 

1411 res = api.ListEventAttendees( 

1412 events_pb2.ListEventAttendeesReq(event_id=event_id, page_size=2, page_token=res.next_page_token) 

1413 ) 

1414 assert res.attendee_user_ids == [user5.id] 

1415 assert not res.next_page_token 

1416 

1417 

1418def test_ListEventSubscribers(db): 

1419 # event creator 

1420 user1, token1 = generate_user() 

1421 # others 

1422 user2, token2 = generate_user() 

1423 user3, token3 = generate_user() 

1424 user4, token4 = generate_user() 

1425 user5, token5 = generate_user() 

1426 user6, token6 = generate_user() 

1427 

1428 with session_scope() as session: 

1429 c_id = create_community(session, 0, 2, "Community", [user1], [], None).id 

1430 

1431 with events_session(token1) as api: 

1432 event_id = api.CreateEvent( 

1433 events_pb2.CreateEventReq( 

1434 title="Dummy Title", 

1435 content="Dummy content.", 

1436 offline_information=events_pb2.OfflineEventInformation( 

1437 address="Near Null Island", 

1438 lat=0.1, 

1439 lng=0.2, 

1440 ), 

1441 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1442 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1443 timezone="UTC", 

1444 ) 

1445 ).event_id 

1446 

1447 for token in [token2, token3, token4, token5]: 

1448 with events_session(token) as api: 

1449 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1450 

1451 with events_session(token6) as api: 

1452 assert api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).subscriber_count == 5 

1453 

1454 res = api.ListEventSubscribers(events_pb2.ListEventSubscribersReq(event_id=event_id, page_size=2)) 

1455 assert res.subscriber_user_ids == [user1.id, user2.id] 

1456 

1457 res = api.ListEventSubscribers( 

1458 events_pb2.ListEventSubscribersReq(event_id=event_id, page_size=2, page_token=res.next_page_token) 

1459 ) 

1460 assert res.subscriber_user_ids == [user3.id, user4.id] 

1461 

1462 res = api.ListEventSubscribers( 

1463 events_pb2.ListEventSubscribersReq(event_id=event_id, page_size=2, page_token=res.next_page_token) 

1464 ) 

1465 assert res.subscriber_user_ids == [user5.id] 

1466 assert not res.next_page_token 

1467 

1468 

1469def test_ListEventOrganizers(db): 

1470 # event creator 

1471 user1, token1 = generate_user() 

1472 # others 

1473 user2, token2 = generate_user() 

1474 user3, token3 = generate_user() 

1475 user4, token4 = generate_user() 

1476 user5, token5 = generate_user() 

1477 user6, token6 = generate_user() 

1478 

1479 with session_scope() as session: 

1480 c_id = create_community(session, 0, 2, "Community", [user1], [], None).id 

1481 

1482 with events_session(token1) as api: 

1483 event_id = api.CreateEvent( 

1484 events_pb2.CreateEventReq( 

1485 title="Dummy Title", 

1486 content="Dummy content.", 

1487 offline_information=events_pb2.OfflineEventInformation( 

1488 address="Near Null Island", 

1489 lat=0.1, 

1490 lng=0.2, 

1491 ), 

1492 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1493 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1494 timezone="UTC", 

1495 ) 

1496 ).event_id 

1497 

1498 with events_session(token1) as api: 

1499 for user_id in [user2.id, user3.id, user4.id, user5.id]: 

1500 api.InviteEventOrganizer(events_pb2.InviteEventOrganizerReq(event_id=event_id, user_id=user_id)) 

1501 

1502 with events_session(token6) as api: 

1503 assert api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).organizer_count == 5 

1504 

1505 res = api.ListEventOrganizers(events_pb2.ListEventOrganizersReq(event_id=event_id, page_size=2)) 

1506 assert res.organizer_user_ids == [user1.id, user2.id] 

1507 

1508 res = api.ListEventOrganizers( 

1509 events_pb2.ListEventOrganizersReq(event_id=event_id, page_size=2, page_token=res.next_page_token) 

1510 ) 

1511 assert res.organizer_user_ids == [user3.id, user4.id] 

1512 

1513 res = api.ListEventOrganizers( 

1514 events_pb2.ListEventOrganizersReq(event_id=event_id, page_size=2, page_token=res.next_page_token) 

1515 ) 

1516 assert res.organizer_user_ids == [user5.id] 

1517 assert not res.next_page_token 

1518 

1519 

1520def test_TransferEvent(db): 

1521 user1, token1 = generate_user() 

1522 user2, token2 = generate_user() 

1523 user3, token3 = generate_user() 

1524 user4, token4 = generate_user() 

1525 

1526 with session_scope() as session: 

1527 c = create_community(session, 0, 2, "Community", [user3], [], None) 

1528 h = create_group(session, "Group", [user4], [], c) 

1529 c_id = c.id 

1530 h_id = h.id 

1531 

1532 with events_session(token1) as api: 

1533 event_id = api.CreateEvent( 

1534 events_pb2.CreateEventReq( 

1535 title="Dummy Title", 

1536 content="Dummy content.", 

1537 offline_information=events_pb2.OfflineEventInformation( 

1538 address="Near Null Island", 

1539 lat=0.1, 

1540 lng=0.2, 

1541 ), 

1542 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1543 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1544 timezone="UTC", 

1545 ) 

1546 ).event_id 

1547 

1548 api.TransferEvent( 

1549 events_pb2.TransferEventReq( 

1550 event_id=event_id, 

1551 new_owner_community_id=c_id, 

1552 ) 

1553 ) 

1554 

1555 with pytest.raises(grpc.RpcError) as e: 

1556 api.TransferEvent( 

1557 events_pb2.TransferEventReq( 

1558 event_id=event_id, 

1559 new_owner_group_id=h_id, 

1560 ) 

1561 ) 

1562 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1563 assert e.value.details() == errors.EVENT_TRANSFER_PERMISSION_DENIED 

1564 

1565 event_id = api.CreateEvent( 

1566 events_pb2.CreateEventReq( 

1567 title="Dummy Title", 

1568 content="Dummy content.", 

1569 offline_information=events_pb2.OfflineEventInformation( 

1570 address="Near Null Island", 

1571 lat=0.1, 

1572 lng=0.2, 

1573 ), 

1574 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1575 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1576 timezone="UTC", 

1577 ) 

1578 ).event_id 

1579 

1580 api.TransferEvent( 

1581 events_pb2.TransferEventReq( 

1582 event_id=event_id, 

1583 new_owner_group_id=h_id, 

1584 ) 

1585 ) 

1586 

1587 with pytest.raises(grpc.RpcError) as e: 

1588 api.TransferEvent( 

1589 events_pb2.TransferEventReq( 

1590 event_id=event_id, 

1591 new_owner_community_id=c_id, 

1592 ) 

1593 ) 

1594 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1595 assert e.value.details() == errors.EVENT_TRANSFER_PERMISSION_DENIED 

1596 

1597 

1598def test_SetEventSubscription(db): 

1599 user1, token1 = generate_user() 

1600 user2, token2 = generate_user() 

1601 

1602 with session_scope() as session: 

1603 c_id = create_community(session, 0, 2, "Community", [user1], [], None).id 

1604 

1605 with events_session(token1) as api: 

1606 event_id = api.CreateEvent( 

1607 events_pb2.CreateEventReq( 

1608 title="Dummy Title", 

1609 content="Dummy content.", 

1610 offline_information=events_pb2.OfflineEventInformation( 

1611 address="Near Null Island", 

1612 lat=0.1, 

1613 lng=0.2, 

1614 ), 

1615 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1616 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1617 timezone="UTC", 

1618 ) 

1619 ).event_id 

1620 

1621 with events_session(token2) as api: 

1622 assert not api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).subscriber 

1623 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=True)) 

1624 assert api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).subscriber 

1625 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=event_id, subscribe=False)) 

1626 assert not api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).subscriber 

1627 

1628 

1629def test_SetEventAttendance(db): 

1630 user1, token1 = generate_user() 

1631 user2, token2 = generate_user() 

1632 

1633 with session_scope() as session: 

1634 c_id = create_community(session, 0, 2, "Community", [user1], [], None).id 

1635 

1636 with events_session(token1) as api: 

1637 event_id = api.CreateEvent( 

1638 events_pb2.CreateEventReq( 

1639 title="Dummy Title", 

1640 content="Dummy content.", 

1641 offline_information=events_pb2.OfflineEventInformation( 

1642 address="Near Null Island", 

1643 lat=0.1, 

1644 lng=0.2, 

1645 ), 

1646 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1647 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1648 timezone="UTC", 

1649 ) 

1650 ).event_id 

1651 

1652 with events_session(token2) as api: 

1653 assert ( 

1654 api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).attendance_state 

1655 == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1656 ) 

1657 api.SetEventAttendance( 

1658 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

1659 ) 

1660 assert ( 

1661 api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).attendance_state 

1662 == events_pb2.ATTENDANCE_STATE_GOING 

1663 ) 

1664 api.SetEventAttendance( 

1665 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_MAYBE) 

1666 ) 

1667 assert ( 

1668 api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).attendance_state 

1669 == events_pb2.ATTENDANCE_STATE_MAYBE 

1670 ) 

1671 api.SetEventAttendance( 

1672 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_NOT_GOING) 

1673 ) 

1674 assert ( 

1675 api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).attendance_state 

1676 == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1677 ) 

1678 

1679 

1680def test_InviteEventOrganizer(db): 

1681 user1, token1 = generate_user() 

1682 user2, token2 = generate_user() 

1683 

1684 with session_scope() as session: 

1685 c_id = create_community(session, 0, 2, "Community", [user1], [], None).id 

1686 

1687 with events_session(token1) as api: 

1688 event_id = api.CreateEvent( 

1689 events_pb2.CreateEventReq( 

1690 title="Dummy Title", 

1691 content="Dummy content.", 

1692 offline_information=events_pb2.OfflineEventInformation( 

1693 address="Near Null Island", 

1694 lat=0.1, 

1695 lng=0.2, 

1696 ), 

1697 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1698 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1699 timezone="UTC", 

1700 ) 

1701 ).event_id 

1702 

1703 with events_session(token2) as api: 

1704 assert not api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).organizer 

1705 

1706 with pytest.raises(grpc.RpcError) as e: 

1707 api.InviteEventOrganizer(events_pb2.InviteEventOrganizerReq(event_id=event_id, user_id=user1.id)) 

1708 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

1709 assert e.value.details() == errors.EVENT_EDIT_PERMISSION_DENIED 

1710 

1711 assert not api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).organizer 

1712 

1713 with events_session(token1) as api: 

1714 api.InviteEventOrganizer(events_pb2.InviteEventOrganizerReq(event_id=event_id, user_id=user2.id)) 

1715 

1716 with events_session(token2) as api: 

1717 assert api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).organizer 

1718 

1719 

1720def test_ListEventOccurrences(db): 

1721 user1, token1 = generate_user() 

1722 user2, token2 = generate_user() 

1723 user3, token3 = generate_user() 

1724 

1725 with session_scope() as session: 

1726 c_id = create_community(session, 0, 2, "Community", [user2], [], None).id 

1727 

1728 start = now() 

1729 

1730 event_ids = [] 

1731 

1732 with events_session(token1) as api: 

1733 res = api.CreateEvent( 

1734 events_pb2.CreateEventReq( 

1735 title="First occurrence", 

1736 content="Dummy content.", 

1737 parent_community_id=c_id, 

1738 online_information=events_pb2.OnlineEventInformation( 

1739 link="https://couchers.org/meet/", 

1740 ), 

1741 start_time=Timestamp_from_datetime(start + timedelta(hours=1)), 

1742 end_time=Timestamp_from_datetime(start + timedelta(hours=1.5)), 

1743 timezone="UTC", 

1744 ) 

1745 ) 

1746 

1747 event_ids.append(res.event_id) 

1748 

1749 for i in range(5): 

1750 res = api.ScheduleEvent( 

1751 events_pb2.ScheduleEventReq( 

1752 event_id=event_ids[-1], 

1753 content=f"{i}th occurrence", 

1754 online_information=events_pb2.OnlineEventInformation( 

1755 link="https://couchers.org/meet/", 

1756 ), 

1757 start_time=Timestamp_from_datetime(start + timedelta(hours=2 + i)), 

1758 end_time=Timestamp_from_datetime(start + timedelta(hours=2.5 + i)), 

1759 timezone="UTC", 

1760 ) 

1761 ) 

1762 

1763 event_ids.append(res.event_id) 

1764 

1765 res = api.ListEventOccurrences(events_pb2.ListEventOccurrencesReq(event_id=event_ids[-1], page_size=2)) 

1766 assert [event.event_id for event in res.events] == event_ids[:2] 

1767 

1768 res = api.ListEventOccurrences( 

1769 events_pb2.ListEventOccurrencesReq(event_id=event_ids[-1], page_size=2, page_token=res.next_page_token) 

1770 ) 

1771 assert [event.event_id for event in res.events] == event_ids[2:4] 

1772 

1773 res = api.ListEventOccurrences( 

1774 events_pb2.ListEventOccurrencesReq(event_id=event_ids[-1], page_size=2, page_token=res.next_page_token) 

1775 ) 

1776 assert [event.event_id for event in res.events] == event_ids[4:6] 

1777 assert not res.next_page_token 

1778 

1779 

1780def test_ListMyEvents(db): 

1781 user1, token1 = generate_user() 

1782 user2, token2 = generate_user() 

1783 user3, token3 = generate_user() 

1784 user4, token4 = generate_user() 

1785 user5, token5 = generate_user() 

1786 

1787 with session_scope() as session: 

1788 c_id = create_community(session, 0, 2, "Community", [user3], [], None).id 

1789 c2_id = create_community(session, 0, 2, "Community", [user4], [], None).id 

1790 

1791 start = now() 

1792 

1793 def new_event(hours_from_now, community_id, online=True): 

1794 if online: 

1795 return events_pb2.CreateEventReq( 

1796 title="Dummy Online Title", 

1797 content="Dummy content.", 

1798 online_information=events_pb2.OnlineEventInformation( 

1799 link="https://couchers.org/meet/", 

1800 ), 

1801 parent_community_id=community_id, 

1802 timezone="UTC", 

1803 start_time=Timestamp_from_datetime(start + timedelta(hours=hours_from_now)), 

1804 end_time=Timestamp_from_datetime(start + timedelta(hours=hours_from_now + 0.5)), 

1805 ) 

1806 else: 

1807 return events_pb2.CreateEventReq( 

1808 title="Dummy Offline Title", 

1809 content="Dummy content.", 

1810 offline_information=events_pb2.OfflineEventInformation( 

1811 address="Near Null Island", 

1812 lat=0.1, 

1813 lng=0.2, 

1814 ), 

1815 timezone="UTC", 

1816 start_time=Timestamp_from_datetime(start + timedelta(hours=hours_from_now)), 

1817 end_time=Timestamp_from_datetime(start + timedelta(hours=hours_from_now + 0.5)), 

1818 ) 

1819 

1820 with events_session(token1) as api: 

1821 e2 = api.CreateEvent(new_event(2, c_id, True)).event_id 

1822 

1823 with events_session(token2) as api: 

1824 e1 = api.CreateEvent(new_event(1, c_id, False)).event_id 

1825 

1826 with events_session(token1) as api: 

1827 e3 = api.CreateEvent(new_event(3, c_id, False)).event_id 

1828 

1829 with events_session(token2) as api: 

1830 e5 = api.CreateEvent(new_event(5, c_id, True)).event_id 

1831 

1832 with events_session(token3) as api: 

1833 e4 = api.CreateEvent(new_event(4, c_id, True)).event_id 

1834 

1835 with events_session(token4) as api: 

1836 e6 = api.CreateEvent(new_event(6, c2_id, True)).event_id 

1837 

1838 with events_session(token1) as api: 

1839 api.InviteEventOrganizer(events_pb2.InviteEventOrganizerReq(event_id=e3, user_id=user3.id)) 

1840 

1841 with events_session(token1) as api: 

1842 api.SetEventAttendance( 

1843 events_pb2.SetEventAttendanceReq(event_id=e1, attendance_state=events_pb2.ATTENDANCE_STATE_MAYBE) 

1844 ) 

1845 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=e4, subscribe=True)) 

1846 

1847 with events_session(token2) as api: 

1848 api.SetEventAttendance( 

1849 events_pb2.SetEventAttendanceReq(event_id=e3, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

1850 ) 

1851 

1852 with events_session(token3) as api: 

1853 api.SetEventSubscription(events_pb2.SetEventSubscriptionReq(event_id=e2, subscribe=True)) 

1854 

1855 with events_session(token1) as api: 

1856 # test pagination with token first 

1857 res = api.ListMyEvents(events_pb2.ListMyEventsReq(page_size=2)) 

1858 assert [event.event_id for event in res.events] == [e1, e2] 

1859 res = api.ListMyEvents(events_pb2.ListMyEventsReq(page_size=2, page_token=res.next_page_token)) 

1860 assert [event.event_id for event in res.events] == [e3, e4] 

1861 assert not res.next_page_token 

1862 

1863 res = api.ListMyEvents( 

1864 events_pb2.ListMyEventsReq( 

1865 subscribed=True, 

1866 attending=True, 

1867 organizing=True, 

1868 ) 

1869 ) 

1870 assert [event.event_id for event in res.events] == [e1, e2, e3, e4] 

1871 

1872 res = api.ListMyEvents(events_pb2.ListMyEventsReq()) 

1873 assert [event.event_id for event in res.events] == [e1, e2, e3, e4] 

1874 

1875 res = api.ListMyEvents(events_pb2.ListMyEventsReq(subscribed=True)) 

1876 assert [event.event_id for event in res.events] == [e2, e3, e4] 

1877 

1878 res = api.ListMyEvents(events_pb2.ListMyEventsReq(attending=True)) 

1879 assert [event.event_id for event in res.events] == [e1, e2, e3] 

1880 

1881 res = api.ListMyEvents(events_pb2.ListMyEventsReq(organizing=True)) 

1882 assert [event.event_id for event in res.events] == [e2, e3] 

1883 

1884 with events_session(token1) as api: 

1885 # Test pagination with page_number and verify total_items 

1886 res = api.ListMyEvents( 

1887 events_pb2.ListMyEventsReq(page_size=2, page_number=1, subscribed=True, attending=True, organizing=True) 

1888 ) 

1889 assert [event.event_id for event in res.events] == [e1, e2] 

1890 assert res.total_items == 4 

1891 

1892 res = api.ListMyEvents( 

1893 events_pb2.ListMyEventsReq(page_size=2, page_number=2, subscribed=True, attending=True, organizing=True) 

1894 ) 

1895 assert [event.event_id for event in res.events] == [e3, e4] 

1896 assert res.total_items == 4 

1897 

1898 # Verify no more pages 

1899 res = api.ListMyEvents( 

1900 events_pb2.ListMyEventsReq(page_size=2, page_number=3, subscribed=True, attending=True, organizing=True) 

1901 ) 

1902 assert not res.events 

1903 assert res.total_items == 4 

1904 

1905 with events_session(token2) as api: 

1906 res = api.ListMyEvents(events_pb2.ListMyEventsReq()) 

1907 assert [event.event_id for event in res.events] == [e1, e3, e5] 

1908 

1909 res = api.ListMyEvents(events_pb2.ListMyEventsReq(subscribed=True)) 

1910 assert [event.event_id for event in res.events] == [e1, e5] 

1911 

1912 res = api.ListMyEvents(events_pb2.ListMyEventsReq(attending=True)) 

1913 assert [event.event_id for event in res.events] == [e1, e3, e5] 

1914 

1915 res = api.ListMyEvents(events_pb2.ListMyEventsReq(organizing=True)) 

1916 assert [event.event_id for event in res.events] == [e1, e5] 

1917 

1918 with events_session(token3) as api: 

1919 res = api.ListMyEvents(events_pb2.ListMyEventsReq()) 

1920 assert [event.event_id for event in res.events] == [e1, e2, e3, e4, e5] 

1921 

1922 res = api.ListMyEvents(events_pb2.ListMyEventsReq(subscribed=True)) 

1923 assert [event.event_id for event in res.events] == [e2, e4] 

1924 

1925 res = api.ListMyEvents(events_pb2.ListMyEventsReq(attending=True)) 

1926 assert [event.event_id for event in res.events] == [e4] 

1927 

1928 res = api.ListMyEvents(events_pb2.ListMyEventsReq(organizing=True)) 

1929 assert [event.event_id for event in res.events] == [e3, e4] 

1930 

1931 res = api.ListMyEvents(events_pb2.ListMyEventsReq(my_communities=True)) 

1932 assert [event.event_id for event in res.events] == [e1, e2, e3, e4, e5] 

1933 

1934 with events_session(token5) as api: 

1935 res = api.ListAllEvents(events_pb2.ListAllEventsReq()) 

1936 assert [event.event_id for event in res.events] == [e1, e2, e3, e4, e5, e6] 

1937 

1938 

1939def test_RemoveEventOrganizer(db): 

1940 user1, token1 = generate_user() 

1941 user2, token2 = generate_user() 

1942 

1943 with session_scope() as session: 

1944 c_id = create_community(session, 0, 2, "Community", [user1], [], None).id 

1945 

1946 with events_session(token1) as api: 

1947 event_id = api.CreateEvent( 

1948 events_pb2.CreateEventReq( 

1949 title="Dummy Title", 

1950 content="Dummy content.", 

1951 offline_information=events_pb2.OfflineEventInformation( 

1952 address="Near Null Island", 

1953 lat=0.1, 

1954 lng=0.2, 

1955 ), 

1956 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

1957 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

1958 timezone="UTC", 

1959 ) 

1960 ).event_id 

1961 

1962 with events_session(token2) as api: 

1963 assert not api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).organizer 

1964 

1965 with pytest.raises(grpc.RpcError) as e: 

1966 api.RemoveEventOrganizer(events_pb2.RemoveEventOrganizerReq(event_id=event_id)) 

1967 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1968 assert e.value.details() == errors.EVENT_NOT_AN_ORGANIZER 

1969 

1970 assert not api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).organizer 

1971 

1972 with events_session(token1) as api: 

1973 api.InviteEventOrganizer(events_pb2.InviteEventOrganizerReq(event_id=event_id, user_id=user2.id)) 

1974 

1975 with pytest.raises(grpc.RpcError) as e: 

1976 api.RemoveEventOrganizer(events_pb2.RemoveEventOrganizerReq(event_id=event_id)) 

1977 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1978 assert e.value.details() == errors.EVENT_CANT_REMOVE_OWNER_AS_ORGANIZER 

1979 

1980 with events_session(token2) as api: 

1981 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

1982 assert res.organizer 

1983 assert res.organizer_count == 2 

1984 api.RemoveEventOrganizer(events_pb2.RemoveEventOrganizerReq(event_id=event_id)) 

1985 assert not api.GetEvent(events_pb2.GetEventReq(event_id=event_id)).organizer 

1986 

1987 with pytest.raises(grpc.RpcError) as e: 

1988 api.RemoveEventOrganizer(events_pb2.RemoveEventOrganizerReq(event_id=event_id)) 

1989 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

1990 assert e.value.details() == errors.EVENT_NOT_AN_ORGANIZER 

1991 

1992 res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

1993 assert not res.organizer 

1994 assert res.organizer_count == 1 

1995 

1996 

1997def test_ListEventAttendees_regression(db): 

1998 # see issue #1617: 

1999 # 

2000 # 1. Create an event 

2001 # 2. Transfer the event to a community (although this step probably not necessarily, only needed for it to show up in UI/`ListEvents` from `communities.proto` 

2002 # 3. Change the current user's attendance state to "not going" (with `SetEventAttendance`) 

2003 # 4. Change the current user's attendance state to "going" again 

2004 # 

2005 # **Expected behaviour** 

2006 # `ListEventAttendees` should return the current user's ID 

2007 # 

2008 # **Actual/current behaviour** 

2009 # `ListEventAttendees` returns another user's ID. This ID seems to be determined from the row's auto increment ID in `event_occurrence_attendees` in the database 

2010 

2011 user1, token1 = generate_user() 

2012 user2, token2 = generate_user() 

2013 user3, token3 = generate_user() 

2014 user4, token4 = generate_user() 

2015 user5, token5 = generate_user() 

2016 

2017 with session_scope() as session: 

2018 c_id = create_community(session, 0, 2, "Community", [user1], [], None).id 

2019 

2020 start_time = now() + timedelta(hours=2) 

2021 end_time = start_time + timedelta(hours=3) 

2022 

2023 with events_session(token1) as api: 

2024 res = api.CreateEvent( 

2025 events_pb2.CreateEventReq( 

2026 title="Dummy Title", 

2027 content="Dummy content.", 

2028 online_information=events_pb2.OnlineEventInformation( 

2029 link="https://couchers.org", 

2030 ), 

2031 parent_community_id=c_id, 

2032 start_time=Timestamp_from_datetime(start_time), 

2033 end_time=Timestamp_from_datetime(end_time), 

2034 timezone="UTC", 

2035 ) 

2036 ) 

2037 

2038 res = api.TransferEvent( 

2039 events_pb2.TransferEventReq( 

2040 event_id=res.event_id, 

2041 new_owner_community_id=c_id, 

2042 ) 

2043 ) 

2044 

2045 event_id = res.event_id 

2046 

2047 api.SetEventAttendance( 

2048 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_NOT_GOING) 

2049 ) 

2050 api.SetEventAttendance( 

2051 events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING) 

2052 ) 

2053 

2054 res = api.ListEventAttendees(events_pb2.ListEventAttendeesReq(event_id=event_id)) 

2055 assert len(res.attendee_user_ids) == 1 

2056 assert res.attendee_user_ids[0] == user1.id 

2057 

2058 

2059def test_event_threads(db): 

2060 user1, token1 = generate_user() 

2061 user2, token2 = generate_user() 

2062 user3, token3 = generate_user() 

2063 user4, token4 = generate_user() 

2064 

2065 with session_scope() as session: 

2066 c = create_community(session, 0, 2, "Community", [user3], [], None) 

2067 h = create_group(session, "Group", [user4], [], c) 

2068 c_id = c.id 

2069 h_id = h.id 

2070 

2071 with events_session(token1) as api: 

2072 event = api.CreateEvent( 

2073 events_pb2.CreateEventReq( 

2074 title="Dummy Title", 

2075 content="Dummy content.", 

2076 offline_information=events_pb2.OfflineEventInformation( 

2077 address="Near Null Island", 

2078 lat=0.1, 

2079 lng=0.2, 

2080 ), 

2081 start_time=Timestamp_from_datetime(now() + timedelta(hours=2)), 

2082 end_time=Timestamp_from_datetime(now() + timedelta(hours=5)), 

2083 timezone="UTC", 

2084 ) 

2085 ) 

2086 

2087 with threads_session(token2) as api: 

2088 reply_id = api.PostReply(threads_pb2.PostReplyReq(thread_id=event.thread.thread_id, content="hi")).thread_id 

2089 

2090 with events_session(token3) as api: 

2091 res = api.GetEvent(events_pb2.GetEventReq(event_id=event.event_id)) 

2092 assert res.thread.num_responses == 1 

2093 

2094 with threads_session(token3) as api: 

2095 ret = api.GetThread(threads_pb2.GetThreadReq(thread_id=res.thread.thread_id)) 

2096 assert len(ret.replies) == 1 

2097 assert not ret.next_page_token 

2098 assert ret.replies[0].thread_id == reply_id 

2099 assert ret.replies[0].content == "hi" 

2100 assert ret.replies[0].author_user_id == user2.id 

2101 assert ret.replies[0].num_replies == 0 

2102 

2103 

2104def test_can_overlap_other_events_schedule_regression(db): 

2105 # we had a bug where we were checking overlapping for *all* occurrences of *all* events, not just the ones for this event 

2106 user, token = generate_user() 

2107 

2108 with session_scope() as session: 

2109 c_id = create_community(session, 0, 2, "Community", [user], [], None).id 

2110 

2111 start = now() 

2112 

2113 with events_session(token) as api: 

2114 # create another event, should be able to overlap with this one 

2115 api.CreateEvent( 

2116 events_pb2.CreateEventReq( 

2117 title="Dummy Title", 

2118 content="Dummy content.", 

2119 parent_community_id=c_id, 

2120 online_information=events_pb2.OnlineEventInformation( 

2121 link="https://couchers.org/meet/", 

2122 ), 

2123 start_time=Timestamp_from_datetime(start + timedelta(hours=1)), 

2124 end_time=Timestamp_from_datetime(start + timedelta(hours=5)), 

2125 timezone="UTC", 

2126 ) 

2127 ) 

2128 

2129 # this event 

2130 res = api.CreateEvent( 

2131 events_pb2.CreateEventReq( 

2132 title="Dummy Title", 

2133 content="Dummy content.", 

2134 parent_community_id=c_id, 

2135 online_information=events_pb2.OnlineEventInformation( 

2136 link="https://couchers.org/meet/", 

2137 ), 

2138 start_time=Timestamp_from_datetime(start + timedelta(hours=1)), 

2139 end_time=Timestamp_from_datetime(start + timedelta(hours=2)), 

2140 timezone="UTC", 

2141 ) 

2142 ) 

2143 

2144 # this doesn't overlap with the just created event, but does overlap with the occurrence from earlier; which should be no problem 

2145 api.ScheduleEvent( 

2146 events_pb2.ScheduleEventReq( 

2147 event_id=res.event_id, 

2148 content="New event occurrence", 

2149 offline_information=events_pb2.OfflineEventInformation( 

2150 address="A bit further but still near Null Island", 

2151 lat=0.3, 

2152 lng=0.2, 

2153 ), 

2154 start_time=Timestamp_from_datetime(start + timedelta(hours=3)), 

2155 end_time=Timestamp_from_datetime(start + timedelta(hours=6)), 

2156 timezone="UTC", 

2157 ) 

2158 ) 

2159 

2160 

2161def test_can_overlap_other_events_update_regression(db): 

2162 user, token = generate_user() 

2163 

2164 with session_scope() as session: 

2165 c_id = create_community(session, 0, 2, "Community", [user], [], None).id 

2166 

2167 start = now() 

2168 

2169 with events_session(token) as api: 

2170 # create another event, should be able to overlap with this one 

2171 api.CreateEvent( 

2172 events_pb2.CreateEventReq( 

2173 title="Dummy Title", 

2174 content="Dummy content.", 

2175 parent_community_id=c_id, 

2176 online_information=events_pb2.OnlineEventInformation( 

2177 link="https://couchers.org/meet/", 

2178 ), 

2179 start_time=Timestamp_from_datetime(start + timedelta(hours=1)), 

2180 end_time=Timestamp_from_datetime(start + timedelta(hours=3)), 

2181 timezone="UTC", 

2182 ) 

2183 ) 

2184 

2185 res = api.CreateEvent( 

2186 events_pb2.CreateEventReq( 

2187 title="Dummy Title", 

2188 content="Dummy content.", 

2189 parent_community_id=c_id, 

2190 online_information=events_pb2.OnlineEventInformation( 

2191 link="https://couchers.org/meet/", 

2192 ), 

2193 start_time=Timestamp_from_datetime(start + timedelta(hours=7)), 

2194 end_time=Timestamp_from_datetime(start + timedelta(hours=8)), 

2195 timezone="UTC", 

2196 ) 

2197 ) 

2198 

2199 event_id = api.ScheduleEvent( 

2200 events_pb2.ScheduleEventReq( 

2201 event_id=res.event_id, 

2202 content="New event occurrence", 

2203 offline_information=events_pb2.OfflineEventInformation( 

2204 address="A bit further but still near Null Island", 

2205 lat=0.3, 

2206 lng=0.2, 

2207 ), 

2208 start_time=Timestamp_from_datetime(start + timedelta(hours=4)), 

2209 end_time=Timestamp_from_datetime(start + timedelta(hours=6)), 

2210 timezone="UTC", 

2211 ) 

2212 ).event_id 

2213 

2214 # can overlap with this current existing occurrence 

2215 api.UpdateEvent( 

2216 events_pb2.UpdateEventReq( 

2217 event_id=event_id, 

2218 start_time=Timestamp_from_datetime(start + timedelta(hours=5)), 

2219 end_time=Timestamp_from_datetime(start + timedelta(hours=6)), 

2220 ) 

2221 ) 

2222 

2223 api.UpdateEvent( 

2224 events_pb2.UpdateEventReq( 

2225 event_id=event_id, 

2226 start_time=Timestamp_from_datetime(start + timedelta(hours=2)), 

2227 end_time=Timestamp_from_datetime(start + timedelta(hours=4)), 

2228 ) 

2229 ) 

2230 

2231 

2232def test_list_past_events_regression(db): 

2233 # test for a bug where listing past events didn't work if they didn't have a future occurrence 

2234 user, token = generate_user() 

2235 

2236 with session_scope() as session: 

2237 c_id = create_community(session, 0, 2, "Community", [user], [], None).id 

2238 

2239 start = now() 

2240 

2241 with events_session(token) as api: 

2242 api.CreateEvent( 

2243 events_pb2.CreateEventReq( 

2244 title="Dummy Title", 

2245 content="Dummy content.", 

2246 parent_community_id=c_id, 

2247 online_information=events_pb2.OnlineEventInformation( 

2248 link="https://couchers.org/meet/", 

2249 ), 

2250 start_time=Timestamp_from_datetime(start + timedelta(hours=3)), 

2251 end_time=Timestamp_from_datetime(start + timedelta(hours=4)), 

2252 timezone="UTC", 

2253 ) 

2254 ) 

2255 

2256 with session_scope() as session: 

2257 session.execute( 

2258 update(EventOccurrence).values( 

2259 during=DateTimeTZRange(start + timedelta(hours=-5), start + timedelta(hours=-4)) 

2260 ) 

2261 ) 

2262 

2263 with events_session(token) as api: 

2264 res = api.ListAllEvents(events_pb2.ListAllEventsReq(past=True)) 

2265 assert len(res.events) == 1 

2266 

2267 

2268def test_community_invite_requests(db): 

2269 user1, token1 = generate_user(complete_profile=True) 

2270 user2, token2 = generate_user() 

2271 user3, token3 = generate_user() 

2272 user4, token4 = generate_user() 

2273 user5, token5 = generate_user(is_superuser=True) 

2274 

2275 with session_scope() as session: 

2276 w = create_community(session, 0, 2, "World Community", [user5], [], None) 

2277 c_id = create_community(session, 0, 2, "Community", [user1, user3, user4], [], w).id 

2278 

2279 enforce_community_memberships() 

2280 

2281 with events_session(token1) as api: 

2282 res = api.CreateEvent( 

2283 events_pb2.CreateEventReq( 

2284 title="Dummy Title", 

2285 content="Dummy content.", 

2286 parent_community_id=c_id, 

2287 online_information=events_pb2.OnlineEventInformation( 

2288 link="https://couchers.org/meet/", 

2289 ), 

2290 start_time=Timestamp_from_datetime(now() + timedelta(hours=3)), 

2291 end_time=Timestamp_from_datetime(now() + timedelta(hours=4)), 

2292 timezone="UTC", 

2293 ) 

2294 ) 

2295 user_url = f"http://localhost:3000/user/{user1.username}" 

2296 event_url = f"http://localhost:3000/event/{res.event_id}/{res.slug}" 

2297 

2298 event_id = res.event_id 

2299 

2300 with mock_notification_email() as mock: 

2301 api.RequestCommunityInvite(events_pb2.RequestCommunityInviteReq(event_id=event_id)) 

2302 assert mock.call_count == 1 

2303 e = email_fields(mock) 

2304 assert e.recipient == "mods@couchers.org.invalid" 

2305 

2306 assert user_url in e.plain 

2307 assert event_url in e.plain 

2308 

2309 # can't send another req 

2310 with pytest.raises(grpc.RpcError) as e: 

2311 api.RequestCommunityInvite(events_pb2.RequestCommunityInviteReq(event_id=event_id)) 

2312 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

2313 assert e.value.details() == errors.EVENT_COMMUNITY_INVITE_ALREADY_REQUESTED 

2314 

2315 # another user can send one though 

2316 with events_session(token3) as api: 

2317 api.RequestCommunityInvite(events_pb2.RequestCommunityInviteReq(event_id=event_id)) 

2318 

2319 # but not a non-admin 

2320 with events_session(token2) as api: 

2321 with pytest.raises(grpc.RpcError) as e: 

2322 api.RequestCommunityInvite(events_pb2.RequestCommunityInviteReq(event_id=event_id)) 

2323 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED 

2324 assert e.value.details() == errors.EVENT_EDIT_PERMISSION_DENIED 

2325 

2326 with real_admin_session(token5) as admin: 

2327 res = admin.ListEventCommunityInviteRequests(admin_pb2.ListEventCommunityInviteRequestsReq()) 

2328 assert len(res.requests) == 2 

2329 assert res.requests[0].user_id == user1.id 

2330 assert res.requests[0].approx_users_to_notify == 3 

2331 assert res.requests[1].user_id == user3.id 

2332 assert res.requests[1].approx_users_to_notify == 3 

2333 

2334 admin.DecideEventCommunityInviteRequest( 

2335 admin_pb2.DecideEventCommunityInviteRequestReq( 

2336 event_community_invite_request_id=res.requests[0].event_community_invite_request_id, 

2337 approve=False, 

2338 ) 

2339 ) 

2340 

2341 admin.DecideEventCommunityInviteRequest( 

2342 admin_pb2.DecideEventCommunityInviteRequestReq( 

2343 event_community_invite_request_id=res.requests[1].event_community_invite_request_id, 

2344 approve=True, 

2345 ) 

2346 ) 

2347 

2348 # not after approve 

2349 with events_session(token4) as api: 

2350 with pytest.raises(grpc.RpcError) as e: 

2351 api.RequestCommunityInvite(events_pb2.RequestCommunityInviteReq(event_id=event_id)) 

2352 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

2353 assert e.value.details() == errors.EVENT_COMMUNITY_INVITE_ALREADY_APPROVED