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

1230 statements  

« prev     ^ index     » next       coverage.py v7.13.2, created at 2026-02-03 06:18 +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.db import session_scope 

10from couchers.models import BackgroundJob, EventOccurrence, Upload 

11from couchers.proto import editor_pb2, events_pb2, threads_pb2 

12from couchers.tasks import enforce_community_memberships 

13from couchers.utils import Timestamp_from_datetime, now, to_aware_datetime 

14from tests.fixtures.db import generate_user 

15from tests.fixtures.misc import PushCollector, email_fields, mock_notification_email, process_jobs 

16from tests.fixtures.sessions import events_session, real_editor_session, threads_session 

17from tests.test_communities import create_community, create_group 

18 

19 

20@pytest.fixture(autouse=True) 

21def _(testconfig): 

22 pass 

23 

24 

25def test_CreateEvent(db, push_collector: PushCollector): 

26 # test cases: 

27 # can create event 

28 # cannot create event with missing details 

29 # can create online event 

30 # can create in person event 

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

32 # can create in different timezones 

33 

34 # event creator 

35 user1, token1 = generate_user() 

36 # community moderator 

37 user2, token2 = generate_user() 

38 # third party 

39 user3, token3 = generate_user() 

40 

41 with session_scope() as session: 

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

43 

44 time_before = now() 

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

46 end_time = start_time + timedelta(hours=3) 

47 

48 with events_session(token1) as api: 

49 # in person event 

50 res = api.CreateEvent( 

51 events_pb2.CreateEventReq( 

52 title="Dummy Title", 

53 content="Dummy content.", 

54 photo_key=None, 

55 offline_information=events_pb2.OfflineEventInformation( 

56 address="Near Null Island", 

57 lat=0.1, 

58 lng=0.2, 

59 ), 

60 start_time=Timestamp_from_datetime(start_time), 

61 end_time=Timestamp_from_datetime(end_time), 

62 timezone="UTC", 

63 ) 

64 ) 

65 

66 assert res.is_next 

67 assert res.title == "Dummy Title" 

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

69 assert res.content == "Dummy content." 

70 assert not res.photo_url 

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

72 assert res.offline_information.lat == 0.1 

73 assert res.offline_information.lng == 0.2 

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

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

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

77 assert res.creator_user_id == user1.id 

78 assert to_aware_datetime(res.start_time) == start_time 

79 assert to_aware_datetime(res.end_time) == end_time 

80 # assert res.timezone == "UTC" 

81 assert res.start_time_display 

82 assert res.end_time_display 

83 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

84 assert res.organizer 

85 assert res.subscriber 

86 assert res.going_count == 1 

87 assert res.maybe_count == 0 

88 assert res.organizer_count == 1 

89 assert res.subscriber_count == 1 

90 assert res.owner_user_id == user1.id 

91 assert not res.owner_community_id 

92 assert not res.owner_group_id 

93 assert res.thread.thread_id 

94 assert res.can_edit 

95 assert not res.can_moderate 

96 

97 event_id = res.event_id 

98 

99 with events_session(token2) as api: 

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

101 

102 assert res.is_next 

103 assert res.title == "Dummy Title" 

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

105 assert res.content == "Dummy content." 

106 assert not res.photo_url 

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

108 assert res.offline_information.lat == 0.1 

109 assert res.offline_information.lng == 0.2 

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

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

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

113 assert res.creator_user_id == user1.id 

114 assert to_aware_datetime(res.start_time) == start_time 

115 assert to_aware_datetime(res.end_time) == end_time 

116 # assert res.timezone == "UTC" 

117 assert res.start_time_display 

118 assert res.end_time_display 

119 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

120 assert not res.organizer 

121 assert not res.subscriber 

122 assert res.going_count == 1 

123 assert res.maybe_count == 0 

124 assert res.organizer_count == 1 

125 assert res.subscriber_count == 1 

126 assert res.owner_user_id == user1.id 

127 assert not res.owner_community_id 

128 assert not res.owner_group_id 

129 assert res.thread.thread_id 

130 assert res.can_edit 

131 assert res.can_moderate 

132 

133 with events_session(token3) as api: 

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

135 

136 assert res.is_next 

137 assert res.title == "Dummy Title" 

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

139 assert res.content == "Dummy content." 

140 assert not res.photo_url 

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

142 assert res.offline_information.lat == 0.1 

143 assert res.offline_information.lng == 0.2 

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

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

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

147 assert res.creator_user_id == user1.id 

148 assert to_aware_datetime(res.start_time) == start_time 

149 assert to_aware_datetime(res.end_time) == end_time 

150 # assert res.timezone == "UTC" 

151 assert res.start_time_display 

152 assert res.end_time_display 

153 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

154 assert not res.organizer 

155 assert not res.subscriber 

156 assert res.going_count == 1 

157 assert res.maybe_count == 0 

158 assert res.organizer_count == 1 

159 assert res.subscriber_count == 1 

160 assert res.owner_user_id == user1.id 

161 assert not res.owner_community_id 

162 assert not res.owner_group_id 

163 assert res.thread.thread_id 

164 assert not res.can_edit 

165 assert not res.can_moderate 

166 

167 with events_session(token1) as api: 

168 # online only event 

169 res = api.CreateEvent( 

170 events_pb2.CreateEventReq( 

171 title="Dummy Title", 

172 content="Dummy content.", 

173 photo_key=None, 

174 online_information=events_pb2.OnlineEventInformation( 

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

176 ), 

177 parent_community_id=c_id, 

178 start_time=Timestamp_from_datetime(start_time), 

179 end_time=Timestamp_from_datetime(end_time), 

180 timezone="UTC", 

181 ) 

182 ) 

183 

184 assert res.is_next 

185 assert res.title == "Dummy Title" 

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

187 assert res.content == "Dummy content." 

188 assert not res.photo_url 

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

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

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

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

193 assert res.creator_user_id == user1.id 

194 assert to_aware_datetime(res.start_time) == start_time 

195 assert to_aware_datetime(res.end_time) == end_time 

196 # assert res.timezone == "UTC" 

197 assert res.start_time_display 

198 assert res.end_time_display 

199 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

200 assert res.organizer 

201 assert res.subscriber 

202 assert res.going_count == 1 

203 assert res.maybe_count == 0 

204 assert res.organizer_count == 1 

205 assert res.subscriber_count == 1 

206 assert res.owner_user_id == user1.id 

207 assert not res.owner_community_id 

208 assert not res.owner_group_id 

209 assert res.thread.thread_id 

210 assert res.can_edit 

211 assert not res.can_moderate 

212 

213 event_id = res.event_id 

214 

215 with events_session(token2) as api: 

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

217 

218 assert res.is_next 

219 assert res.title == "Dummy Title" 

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

221 assert res.content == "Dummy content." 

222 assert not res.photo_url 

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

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

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

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

227 assert res.creator_user_id == user1.id 

228 assert to_aware_datetime(res.start_time) == start_time 

229 assert to_aware_datetime(res.end_time) == end_time 

230 # assert res.timezone == "UTC" 

231 assert res.start_time_display 

232 assert res.end_time_display 

233 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

234 assert not res.organizer 

235 assert not res.subscriber 

236 assert res.going_count == 1 

237 assert res.maybe_count == 0 

238 assert res.organizer_count == 1 

239 assert res.subscriber_count == 1 

240 assert res.owner_user_id == user1.id 

241 assert not res.owner_community_id 

242 assert not res.owner_group_id 

243 assert res.thread.thread_id 

244 assert res.can_edit 

245 assert res.can_moderate 

246 

247 with events_session(token3) as api: 

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

249 

250 assert res.is_next 

251 assert res.title == "Dummy Title" 

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

253 assert res.content == "Dummy content." 

254 assert not res.photo_url 

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

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

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

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

259 assert res.creator_user_id == user1.id 

260 assert to_aware_datetime(res.start_time) == start_time 

261 assert to_aware_datetime(res.end_time) == end_time 

262 # assert res.timezone == "UTC" 

263 assert res.start_time_display 

264 assert res.end_time_display 

265 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

266 assert not res.organizer 

267 assert not res.subscriber 

268 assert res.going_count == 1 

269 assert res.maybe_count == 0 

270 assert res.organizer_count == 1 

271 assert res.subscriber_count == 1 

272 assert res.owner_user_id == user1.id 

273 assert not res.owner_community_id 

274 assert not res.owner_group_id 

275 assert res.thread.thread_id 

276 assert not res.can_edit 

277 assert not res.can_moderate 

278 

279 with events_session(token1) as api: 

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

281 api.CreateEvent( 

282 events_pb2.CreateEventReq( 

283 title="Dummy Title", 

284 content="Dummy content.", 

285 photo_key=None, 

286 online_information=events_pb2.OnlineEventInformation( 

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

288 ), 

289 start_time=Timestamp_from_datetime(start_time), 

290 end_time=Timestamp_from_datetime(end_time), 

291 timezone="UTC", 

292 ) 

293 ) 

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

295 assert e.value.details() == "The online event is missing a parent community." 

296 

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

298 api.CreateEvent( 

299 events_pb2.CreateEventReq( 

300 # title="Dummy Title", 

301 content="Dummy content.", 

302 photo_key=None, 

303 offline_information=events_pb2.OfflineEventInformation( 

304 address="Near Null Island", 

305 lat=0.1, 

306 lng=0.1, 

307 ), 

308 start_time=Timestamp_from_datetime(start_time), 

309 end_time=Timestamp_from_datetime(end_time), 

310 timezone="UTC", 

311 ) 

312 ) 

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

314 assert e.value.details() == "Missing event title." 

315 

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

317 api.CreateEvent( 

318 events_pb2.CreateEventReq( 

319 title="Dummy Title", 

320 # content="Dummy content.", 

321 photo_key=None, 

322 offline_information=events_pb2.OfflineEventInformation( 

323 address="Near Null Island", 

324 lat=0.1, 

325 lng=0.1, 

326 ), 

327 start_time=Timestamp_from_datetime(start_time), 

328 end_time=Timestamp_from_datetime(end_time), 

329 timezone="UTC", 

330 ) 

331 ) 

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

333 assert e.value.details() == "Missing event content." 

334 

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

336 api.CreateEvent( 

337 events_pb2.CreateEventReq( 

338 title="Dummy Title", 

339 content="Dummy content.", 

340 photo_key="nonexistent", 

341 offline_information=events_pb2.OfflineEventInformation( 

342 address="Near Null Island", 

343 lat=0.1, 

344 lng=0.1, 

345 ), 

346 start_time=Timestamp_from_datetime(start_time), 

347 end_time=Timestamp_from_datetime(end_time), 

348 timezone="UTC", 

349 ) 

350 ) 

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

352 assert e.value.details() == "Photo not found." 

353 

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

355 api.CreateEvent( 

356 events_pb2.CreateEventReq( 

357 title="Dummy Title", 

358 content="Dummy content.", 

359 photo_key=None, 

360 offline_information=events_pb2.OfflineEventInformation( 

361 address="Near Null Island", 

362 ), 

363 start_time=Timestamp_from_datetime(start_time), 

364 end_time=Timestamp_from_datetime(end_time), 

365 timezone="UTC", 

366 ) 

367 ) 

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

369 assert e.value.details() == "Missing event address or location." 

370 

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

372 api.CreateEvent( 

373 events_pb2.CreateEventReq( 

374 title="Dummy Title", 

375 content="Dummy content.", 

376 photo_key=None, 

377 offline_information=events_pb2.OfflineEventInformation( 

378 lat=0.1, 

379 lng=0.1, 

380 ), 

381 start_time=Timestamp_from_datetime(start_time), 

382 end_time=Timestamp_from_datetime(end_time), 

383 timezone="UTC", 

384 ) 

385 ) 

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

387 assert e.value.details() == "Missing event address or location." 

388 

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

390 api.CreateEvent( 

391 events_pb2.CreateEventReq( 

392 title="Dummy Title", 

393 content="Dummy content.", 

394 photo_key=None, 

395 online_information=events_pb2.OnlineEventInformation(), 

396 start_time=Timestamp_from_datetime(start_time), 

397 end_time=Timestamp_from_datetime(end_time), 

398 timezone="UTC", 

399 ) 

400 ) 

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

402 assert e.value.details() == "An online-only event requires a link." 

403 

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

405 api.CreateEvent( 

406 events_pb2.CreateEventReq( 

407 title="Dummy Title", 

408 content="Dummy content.", 

409 parent_community_id=c_id, 

410 online_information=events_pb2.OnlineEventInformation( 

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

412 ), 

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

414 end_time=Timestamp_from_datetime(end_time), 

415 timezone="UTC", 

416 ) 

417 ) 

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

419 assert e.value.details() == "The event must be in the future." 

420 

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

422 api.CreateEvent( 

423 events_pb2.CreateEventReq( 

424 title="Dummy Title", 

425 content="Dummy content.", 

426 parent_community_id=c_id, 

427 online_information=events_pb2.OnlineEventInformation( 

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

429 ), 

430 start_time=Timestamp_from_datetime(end_time), 

431 end_time=Timestamp_from_datetime(start_time), 

432 timezone="UTC", 

433 ) 

434 ) 

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

436 assert e.value.details() == "The event must end after it starts." 

437 

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

439 api.CreateEvent( 

440 events_pb2.CreateEventReq( 

441 title="Dummy Title", 

442 content="Dummy content.", 

443 parent_community_id=c_id, 

444 online_information=events_pb2.OnlineEventInformation( 

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

446 ), 

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

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

449 timezone="UTC", 

450 ) 

451 ) 

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

453 assert e.value.details() == "The event needs to start within the next year." 

454 

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

456 api.CreateEvent( 

457 events_pb2.CreateEventReq( 

458 title="Dummy Title", 

459 content="Dummy content.", 

460 parent_community_id=c_id, 

461 online_information=events_pb2.OnlineEventInformation( 

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

463 ), 

464 start_time=Timestamp_from_datetime(start_time), 

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

466 timezone="UTC", 

467 ) 

468 ) 

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

470 assert e.value.details() == "Events cannot last longer than 7 days." 

471 

472 

473def test_CreateEvent_incomplete_profile(db): 

474 user1, token1 = generate_user(complete_profile=False) 

475 user2, token2 = generate_user() 

476 

477 with session_scope() as session: 

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

479 

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

481 end_time = start_time + timedelta(hours=3) 

482 

483 with events_session(token1) as api: 

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

485 api.CreateEvent( 

486 events_pb2.CreateEventReq( 

487 title="Dummy Title", 

488 content="Dummy content.", 

489 photo_key=None, 

490 offline_information=events_pb2.OfflineEventInformation( 

491 address="Near Null Island", 

492 lat=0.1, 

493 lng=0.2, 

494 ), 

495 start_time=Timestamp_from_datetime(start_time), 

496 end_time=Timestamp_from_datetime(end_time), 

497 timezone="UTC", 

498 ) 

499 ) 

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

501 assert e.value.details() == "You have to complete your profile before you can create an event." 

502 

503 

504def test_ScheduleEvent(db): 

505 # test cases: 

506 # can schedule a new event occurrence 

507 

508 user, token = generate_user() 

509 

510 with session_scope() as session: 

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

512 

513 time_before = now() 

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

515 end_time = start_time + timedelta(hours=3) 

516 

517 with events_session(token) as api: 

518 res = api.CreateEvent( 

519 events_pb2.CreateEventReq( 

520 title="Dummy Title", 

521 content="Dummy content.", 

522 parent_community_id=c_id, 

523 online_information=events_pb2.OnlineEventInformation( 

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

525 ), 

526 start_time=Timestamp_from_datetime(start_time), 

527 end_time=Timestamp_from_datetime(end_time), 

528 timezone="UTC", 

529 ) 

530 ) 

531 

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

533 new_end_time = new_start_time + timedelta(hours=2) 

534 

535 res = api.ScheduleEvent( 

536 events_pb2.ScheduleEventReq( 

537 event_id=res.event_id, 

538 content="New event occurrence", 

539 offline_information=events_pb2.OfflineEventInformation( 

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

541 lat=0.3, 

542 lng=0.2, 

543 ), 

544 start_time=Timestamp_from_datetime(new_start_time), 

545 end_time=Timestamp_from_datetime(new_end_time), 

546 timezone="UTC", 

547 ) 

548 ) 

549 

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

551 

552 assert not res.is_next 

553 assert res.title == "Dummy Title" 

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

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

556 assert not res.photo_url 

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

558 assert res.offline_information.lat == 0.3 

559 assert res.offline_information.lng == 0.2 

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

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

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

563 assert res.creator_user_id == user.id 

564 assert to_aware_datetime(res.start_time) == new_start_time 

565 assert to_aware_datetime(res.end_time) == new_end_time 

566 # assert res.timezone == "UTC" 

567 assert res.start_time_display 

568 assert res.end_time_display 

569 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

570 assert res.organizer 

571 assert res.subscriber 

572 assert res.going_count == 1 

573 assert res.maybe_count == 0 

574 assert res.organizer_count == 1 

575 assert res.subscriber_count == 1 

576 assert res.owner_user_id == user.id 

577 assert not res.owner_community_id 

578 assert not res.owner_group_id 

579 assert res.thread.thread_id 

580 assert res.can_edit 

581 assert res.can_moderate 

582 

583 

584def test_cannot_overlap_occurrences_schedule(db): 

585 user, token = generate_user() 

586 

587 with session_scope() as session: 

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

589 

590 start = now() 

591 

592 with events_session(token) as api: 

593 res = api.CreateEvent( 

594 events_pb2.CreateEventReq( 

595 title="Dummy Title", 

596 content="Dummy content.", 

597 parent_community_id=c_id, 

598 online_information=events_pb2.OnlineEventInformation( 

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

600 ), 

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

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

603 timezone="UTC", 

604 ) 

605 ) 

606 

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

608 api.ScheduleEvent( 

609 events_pb2.ScheduleEventReq( 

610 event_id=res.event_id, 

611 content="New event occurrence", 

612 offline_information=events_pb2.OfflineEventInformation( 

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

614 lat=0.3, 

615 lng=0.2, 

616 ), 

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

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

619 timezone="UTC", 

620 ) 

621 ) 

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

623 assert e.value.details() == "An event cannot have overlapping occurrences." 

624 

625 

626def test_cannot_overlap_occurrences_update(db): 

627 user, token = generate_user() 

628 

629 with session_scope() as session: 

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

631 

632 start = now() 

633 

634 with events_session(token) as api: 

635 res = api.CreateEvent( 

636 events_pb2.CreateEventReq( 

637 title="Dummy Title", 

638 content="Dummy content.", 

639 parent_community_id=c_id, 

640 online_information=events_pb2.OnlineEventInformation( 

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

642 ), 

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

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

645 timezone="UTC", 

646 ) 

647 ) 

648 

649 event_id = api.ScheduleEvent( 

650 events_pb2.ScheduleEventReq( 

651 event_id=res.event_id, 

652 content="New event occurrence", 

653 offline_information=events_pb2.OfflineEventInformation( 

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

655 lat=0.3, 

656 lng=0.2, 

657 ), 

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

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

660 timezone="UTC", 

661 ) 

662 ).event_id 

663 

664 # can overlap with this current existing occurrence 

665 api.UpdateEvent( 

666 events_pb2.UpdateEventReq( 

667 event_id=event_id, 

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

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

670 ) 

671 ) 

672 

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

674 api.UpdateEvent( 

675 events_pb2.UpdateEventReq( 

676 event_id=event_id, 

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

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

679 ) 

680 ) 

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

682 assert e.value.details() == "An event cannot have overlapping occurrences." 

683 

684 

685def test_UpdateEvent_single(db): 

686 # test cases: 

687 # owner can update 

688 # community owner can update 

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

690 # notifies attendees 

691 

692 # event creator 

693 user1, token1 = generate_user() 

694 # community moderator 

695 user2, token2 = generate_user() 

696 # third parties 

697 user3, token3 = generate_user() 

698 user4, token4 = generate_user() 

699 user5, token5 = generate_user() 

700 user6, token6 = generate_user() 

701 

702 with session_scope() as session: 

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

704 

705 time_before = now() 

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

707 end_time = start_time + timedelta(hours=3) 

708 

709 with events_session(token1) as api: 

710 res = api.CreateEvent( 

711 events_pb2.CreateEventReq( 

712 title="Dummy Title", 

713 content="Dummy content.", 

714 offline_information=events_pb2.OfflineEventInformation( 

715 address="Near Null Island", 

716 lat=0.1, 

717 lng=0.2, 

718 ), 

719 start_time=Timestamp_from_datetime(start_time), 

720 end_time=Timestamp_from_datetime(end_time), 

721 timezone="UTC", 

722 ) 

723 ) 

724 

725 event_id = res.event_id 

726 

727 with events_session(token4) as api: 

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

729 

730 with events_session(token5) as api: 

731 api.SetEventAttendance( 

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

733 ) 

734 

735 with events_session(token6) as api: 

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

737 api.SetEventAttendance( 

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

739 ) 

740 

741 time_before_update = now() 

742 

743 with events_session(token1) as api: 

744 res = api.UpdateEvent( 

745 events_pb2.UpdateEventReq( 

746 event_id=event_id, 

747 ) 

748 ) 

749 

750 with events_session(token1) as api: 

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

752 

753 assert res.is_next 

754 assert res.title == "Dummy Title" 

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

756 assert res.content == "Dummy content." 

757 assert not res.photo_url 

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

759 assert res.offline_information.lat == 0.1 

760 assert res.offline_information.lng == 0.2 

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

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

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

764 assert res.creator_user_id == user1.id 

765 assert to_aware_datetime(res.start_time) == start_time 

766 assert to_aware_datetime(res.end_time) == end_time 

767 # assert res.timezone == "UTC" 

768 assert res.start_time_display 

769 assert res.end_time_display 

770 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

771 assert res.organizer 

772 assert res.subscriber 

773 assert res.going_count == 2 

774 assert res.maybe_count == 1 

775 assert res.organizer_count == 1 

776 assert res.subscriber_count == 3 

777 assert res.owner_user_id == user1.id 

778 assert not res.owner_community_id 

779 assert not res.owner_group_id 

780 assert res.thread.thread_id 

781 assert res.can_edit 

782 assert not res.can_moderate 

783 

784 with events_session(token2) as api: 

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

786 

787 assert res.is_next 

788 assert res.title == "Dummy Title" 

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

790 assert res.content == "Dummy content." 

791 assert not res.photo_url 

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

793 assert res.offline_information.lat == 0.1 

794 assert res.offline_information.lng == 0.2 

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

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

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

798 assert res.creator_user_id == user1.id 

799 assert to_aware_datetime(res.start_time) == start_time 

800 assert to_aware_datetime(res.end_time) == end_time 

801 # assert res.timezone == "UTC" 

802 assert res.start_time_display 

803 assert res.end_time_display 

804 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

805 assert not res.organizer 

806 assert not res.subscriber 

807 assert res.going_count == 2 

808 assert res.maybe_count == 1 

809 assert res.organizer_count == 1 

810 assert res.subscriber_count == 3 

811 assert res.owner_user_id == user1.id 

812 assert not res.owner_community_id 

813 assert not res.owner_group_id 

814 assert res.thread.thread_id 

815 assert res.can_edit 

816 assert res.can_moderate 

817 

818 with events_session(token3) as api: 

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

820 

821 assert res.is_next 

822 assert res.title == "Dummy Title" 

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

824 assert res.content == "Dummy content." 

825 assert not res.photo_url 

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

827 assert res.offline_information.lat == 0.1 

828 assert res.offline_information.lng == 0.2 

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

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

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

832 assert res.creator_user_id == user1.id 

833 assert to_aware_datetime(res.start_time) == start_time 

834 assert to_aware_datetime(res.end_time) == end_time 

835 # assert res.timezone == "UTC" 

836 assert res.start_time_display 

837 assert res.end_time_display 

838 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

839 assert not res.organizer 

840 assert not res.subscriber 

841 assert res.going_count == 2 

842 assert res.maybe_count == 1 

843 assert res.organizer_count == 1 

844 assert res.subscriber_count == 3 

845 assert res.owner_user_id == user1.id 

846 assert not res.owner_community_id 

847 assert not res.owner_group_id 

848 assert res.thread.thread_id 

849 assert not res.can_edit 

850 assert not res.can_moderate 

851 

852 with events_session(token1) as api: 

853 res = api.UpdateEvent( 

854 events_pb2.UpdateEventReq( 

855 event_id=event_id, 

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

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

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

859 start_time=Timestamp_from_datetime(start_time), 

860 end_time=Timestamp_from_datetime(end_time), 

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

862 ) 

863 ) 

864 

865 assert res.is_next 

866 assert res.title == "Dummy Title" 

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

868 assert res.content == "Dummy content." 

869 assert not res.photo_url 

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

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

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

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

874 assert res.creator_user_id == user1.id 

875 assert to_aware_datetime(res.start_time) == start_time 

876 assert to_aware_datetime(res.end_time) == end_time 

877 # assert res.timezone == "UTC" 

878 assert res.start_time_display 

879 assert res.end_time_display 

880 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

881 assert res.organizer 

882 assert res.subscriber 

883 assert res.going_count == 2 

884 assert res.maybe_count == 1 

885 assert res.organizer_count == 1 

886 assert res.subscriber_count == 3 

887 assert res.owner_user_id == user1.id 

888 assert not res.owner_community_id 

889 assert not res.owner_group_id 

890 assert res.thread.thread_id 

891 assert res.can_edit 

892 assert not res.can_moderate 

893 

894 event_id = res.event_id 

895 

896 with events_session(token2) as api: 

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

898 

899 assert res.is_next 

900 assert res.title == "Dummy Title" 

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

902 assert res.content == "Dummy content." 

903 assert not res.photo_url 

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

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

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

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

908 assert res.creator_user_id == user1.id 

909 assert to_aware_datetime(res.start_time) == start_time 

910 assert to_aware_datetime(res.end_time) == end_time 

911 # assert res.timezone == "UTC" 

912 assert res.start_time_display 

913 assert res.end_time_display 

914 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

915 assert not res.organizer 

916 assert not res.subscriber 

917 assert res.going_count == 2 

918 assert res.maybe_count == 1 

919 assert res.organizer_count == 1 

920 assert res.subscriber_count == 3 

921 assert res.owner_user_id == user1.id 

922 assert not res.owner_community_id 

923 assert not res.owner_group_id 

924 assert res.thread.thread_id 

925 assert res.can_edit 

926 assert res.can_moderate 

927 

928 with events_session(token3) as api: 

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

930 

931 assert res.is_next 

932 assert res.title == "Dummy Title" 

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

934 assert res.content == "Dummy content." 

935 assert not res.photo_url 

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

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

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

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

940 assert res.creator_user_id == user1.id 

941 assert to_aware_datetime(res.start_time) == start_time 

942 assert to_aware_datetime(res.end_time) == end_time 

943 # assert res.timezone == "UTC" 

944 assert res.start_time_display 

945 assert res.end_time_display 

946 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

947 assert not res.organizer 

948 assert not res.subscriber 

949 assert res.going_count == 2 

950 assert res.maybe_count == 1 

951 assert res.organizer_count == 1 

952 assert res.subscriber_count == 3 

953 assert res.owner_user_id == user1.id 

954 assert not res.owner_community_id 

955 assert not res.owner_group_id 

956 assert res.thread.thread_id 

957 assert not res.can_edit 

958 assert not res.can_moderate 

959 

960 with events_session(token1) as api: 

961 res = api.UpdateEvent( 

962 events_pb2.UpdateEventReq( 

963 event_id=event_id, 

964 offline_information=events_pb2.OfflineEventInformation( 

965 address="Near Null Island", 

966 lat=0.1, 

967 lng=0.2, 

968 ), 

969 ) 

970 ) 

971 

972 with events_session(token3) as api: 

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

974 

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

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

977 assert res.offline_information.lat == 0.1 

978 assert res.offline_information.lng == 0.2 

979 

980 

981def test_UpdateEvent_all(db): 

982 # event creator 

983 user1, token1 = generate_user() 

984 # community moderator 

985 user2, token2 = generate_user() 

986 # third parties 

987 user3, token3 = generate_user() 

988 user4, token4 = generate_user() 

989 user5, token5 = generate_user() 

990 user6, token6 = generate_user() 

991 

992 with session_scope() as session: 

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

994 

995 time_before = now() 

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

997 end_time = start_time + timedelta(hours=1.5) 

998 

999 event_ids = [] 

1000 

1001 with events_session(token1) as api: 

1002 res = api.CreateEvent( 

1003 events_pb2.CreateEventReq( 

1004 title="Dummy Title", 

1005 content="0th occurrence", 

1006 offline_information=events_pb2.OfflineEventInformation( 

1007 address="Near Null Island", 

1008 lat=0.1, 

1009 lng=0.2, 

1010 ), 

1011 start_time=Timestamp_from_datetime(start_time), 

1012 end_time=Timestamp_from_datetime(end_time), 

1013 timezone="UTC", 

1014 ) 

1015 ) 

1016 

1017 event_id = res.event_id 

1018 event_ids.append(event_id) 

1019 

1020 with events_session(token4) as api: 

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

1022 

1023 with events_session(token5) as api: 

1024 api.SetEventAttendance( 

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

1026 ) 

1027 

1028 with events_session(token6) as api: 

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

1030 api.SetEventAttendance( 

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

1032 ) 

1033 

1034 with events_session(token1) as api: 

1035 for i in range(5): 

1036 res = api.ScheduleEvent( 

1037 events_pb2.ScheduleEventReq( 

1038 event_id=event_ids[-1], 

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

1040 online_information=events_pb2.OnlineEventInformation( 

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

1042 ), 

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

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

1045 timezone="UTC", 

1046 ) 

1047 ) 

1048 

1049 event_ids.append(res.event_id) 

1050 

1051 updated_event_id = event_ids[3] 

1052 

1053 time_before_update = now() 

1054 

1055 with events_session(token1) as api: 

1056 res = api.UpdateEvent( 

1057 events_pb2.UpdateEventReq( 

1058 event_id=updated_event_id, 

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

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

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

1062 update_all_future=True, 

1063 ) 

1064 ) 

1065 

1066 time_after_update = now() 

1067 

1068 with events_session(token2) as api: 

1069 for i in range(3): 

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

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

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

1073 

1074 for i in range(3, 6): 

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

1076 assert res.content == "New content." 

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

1078 

1079 

1080def test_GetEvent(db): 

1081 # event creator 

1082 user1, token1 = generate_user() 

1083 # community moderator 

1084 user2, token2 = generate_user() 

1085 # third parties 

1086 user3, token3 = generate_user() 

1087 user4, token4 = generate_user() 

1088 user5, token5 = generate_user() 

1089 user6, token6 = generate_user() 

1090 

1091 with session_scope() as session: 

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

1093 

1094 time_before = now() 

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

1096 end_time = start_time + timedelta(hours=3) 

1097 

1098 with events_session(token1) as api: 

1099 # in person event 

1100 res = api.CreateEvent( 

1101 events_pb2.CreateEventReq( 

1102 title="Dummy Title", 

1103 content="Dummy content.", 

1104 offline_information=events_pb2.OfflineEventInformation( 

1105 address="Near Null Island", 

1106 lat=0.1, 

1107 lng=0.2, 

1108 ), 

1109 start_time=Timestamp_from_datetime(start_time), 

1110 end_time=Timestamp_from_datetime(end_time), 

1111 timezone="UTC", 

1112 ) 

1113 ) 

1114 

1115 event_id = res.event_id 

1116 

1117 with events_session(token4) as api: 

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

1119 

1120 with events_session(token5) as api: 

1121 api.SetEventAttendance( 

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

1123 ) 

1124 

1125 with events_session(token6) as api: 

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

1127 api.SetEventAttendance( 

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

1129 ) 

1130 

1131 with events_session(token1) as api: 

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

1133 

1134 assert res.is_next 

1135 assert res.title == "Dummy Title" 

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

1137 assert res.content == "Dummy content." 

1138 assert not res.photo_url 

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

1140 assert res.offline_information.lat == 0.1 

1141 assert res.offline_information.lng == 0.2 

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

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

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

1145 assert res.creator_user_id == user1.id 

1146 assert to_aware_datetime(res.start_time) == start_time 

1147 assert to_aware_datetime(res.end_time) == end_time 

1148 # assert res.timezone == "UTC" 

1149 assert res.start_time_display 

1150 assert res.end_time_display 

1151 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

1152 assert res.organizer 

1153 assert res.subscriber 

1154 assert res.going_count == 2 

1155 assert res.maybe_count == 1 

1156 assert res.organizer_count == 1 

1157 assert res.subscriber_count == 3 

1158 assert res.owner_user_id == user1.id 

1159 assert not res.owner_community_id 

1160 assert not res.owner_group_id 

1161 assert res.thread.thread_id 

1162 assert res.can_edit 

1163 assert not res.can_moderate 

1164 

1165 with events_session(token2) as api: 

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

1167 

1168 assert res.is_next 

1169 assert res.title == "Dummy Title" 

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

1171 assert res.content == "Dummy content." 

1172 assert not res.photo_url 

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

1174 assert res.offline_information.lat == 0.1 

1175 assert res.offline_information.lng == 0.2 

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

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

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

1179 assert res.creator_user_id == user1.id 

1180 assert to_aware_datetime(res.start_time) == start_time 

1181 assert to_aware_datetime(res.end_time) == end_time 

1182 # assert res.timezone == "UTC" 

1183 assert res.start_time_display 

1184 assert res.end_time_display 

1185 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1186 assert not res.organizer 

1187 assert not res.subscriber 

1188 assert res.going_count == 2 

1189 assert res.maybe_count == 1 

1190 assert res.organizer_count == 1 

1191 assert res.subscriber_count == 3 

1192 assert res.owner_user_id == user1.id 

1193 assert not res.owner_community_id 

1194 assert not res.owner_group_id 

1195 assert res.thread.thread_id 

1196 assert res.can_edit 

1197 assert res.can_moderate 

1198 

1199 with events_session(token3) as api: 

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

1201 

1202 assert res.is_next 

1203 assert res.title == "Dummy Title" 

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

1205 assert res.content == "Dummy content." 

1206 assert not res.photo_url 

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

1208 assert res.offline_information.lat == 0.1 

1209 assert res.offline_information.lng == 0.2 

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

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

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

1213 assert res.creator_user_id == user1.id 

1214 assert to_aware_datetime(res.start_time) == start_time 

1215 assert to_aware_datetime(res.end_time) == end_time 

1216 # assert res.timezone == "UTC" 

1217 assert res.start_time_display 

1218 assert res.end_time_display 

1219 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1220 assert not res.organizer 

1221 assert not res.subscriber 

1222 assert res.going_count == 2 

1223 assert res.maybe_count == 1 

1224 assert res.organizer_count == 1 

1225 assert res.subscriber_count == 3 

1226 assert res.owner_user_id == user1.id 

1227 assert not res.owner_community_id 

1228 assert not res.owner_group_id 

1229 assert res.thread.thread_id 

1230 assert not res.can_edit 

1231 assert not res.can_moderate 

1232 

1233 

1234def test_CancelEvent(db): 

1235 # event creator 

1236 user1, token1 = generate_user() 

1237 # community moderator 

1238 user2, token2 = generate_user() 

1239 # third parties 

1240 user3, token3 = generate_user() 

1241 user4, token4 = generate_user() 

1242 user5, token5 = generate_user() 

1243 user6, token6 = generate_user() 

1244 

1245 with session_scope() as session: 

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

1247 

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

1249 end_time = start_time + timedelta(hours=3) 

1250 

1251 with events_session(token1) as api: 

1252 res = api.CreateEvent( 

1253 events_pb2.CreateEventReq( 

1254 title="Dummy Title", 

1255 content="Dummy content.", 

1256 offline_information=events_pb2.OfflineEventInformation( 

1257 address="Near Null Island", 

1258 lat=0.1, 

1259 lng=0.2, 

1260 ), 

1261 start_time=Timestamp_from_datetime(start_time), 

1262 end_time=Timestamp_from_datetime(end_time), 

1263 timezone="UTC", 

1264 ) 

1265 ) 

1266 

1267 event_id = res.event_id 

1268 

1269 with events_session(token4) as api: 

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

1271 

1272 with events_session(token5) as api: 

1273 api.SetEventAttendance( 

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

1275 ) 

1276 

1277 with events_session(token6) as api: 

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

1279 api.SetEventAttendance( 

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

1281 ) 

1282 

1283 with events_session(token1) as api: 

1284 res = api.CancelEvent( 

1285 events_pb2.CancelEventReq( 

1286 event_id=event_id, 

1287 ) 

1288 ) 

1289 

1290 with events_session(token1) as api: 

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

1292 assert res.is_cancelled 

1293 

1294 with events_session(token1) as api: 

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

1296 api.UpdateEvent( 

1297 events_pb2.UpdateEventReq( 

1298 event_id=event_id, 

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

1300 ) 

1301 ) 

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

1303 assert e.value.details() == "You can't modify, subscribe to, or attend to an event that's been cancelled." 

1304 

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

1306 api.InviteEventOrganizer( 

1307 events_pb2.InviteEventOrganizerReq( 

1308 event_id=event_id, 

1309 user_id=user3.id, 

1310 ) 

1311 ) 

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

1313 assert e.value.details() == "You can't modify, subscribe to, or attend to an event that's been cancelled." 

1314 

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

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

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

1318 assert e.value.details() == "You can't modify, subscribe to, or attend to an event that's been cancelled." 

1319 

1320 with events_session(token3) as api: 

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

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

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

1324 assert e.value.details() == "You can't modify, subscribe to, or attend to an event that's been cancelled." 

1325 

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

1327 api.SetEventAttendance( 

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

1329 ) 

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

1331 assert e.value.details() == "You can't modify, subscribe to, or attend to an event that's been cancelled." 

1332 

1333 with events_session(token1) as api: 

1334 for include_cancelled in [True, False]: 

1335 res = api.ListEventOccurrences( 

1336 events_pb2.ListEventOccurrencesReq( 

1337 event_id=event_id, 

1338 include_cancelled=include_cancelled, 

1339 ) 

1340 ) 

1341 if include_cancelled: 

1342 assert len(res.events) > 0 

1343 else: 

1344 assert len(res.events) == 0 

1345 

1346 res = api.ListMyEvents( 

1347 events_pb2.ListMyEventsReq( 

1348 include_cancelled=include_cancelled, 

1349 ) 

1350 ) 

1351 if include_cancelled: 

1352 assert len(res.events) > 0 

1353 else: 

1354 assert len(res.events) == 0 

1355 

1356 

1357def test_ListEventAttendees(db): 

1358 # event creator 

1359 user1, token1 = generate_user() 

1360 # others 

1361 user2, token2 = generate_user() 

1362 user3, token3 = generate_user() 

1363 user4, token4 = generate_user() 

1364 user5, token5 = generate_user() 

1365 user6, token6 = generate_user() 

1366 

1367 with session_scope() as session: 

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

1369 

1370 with events_session(token1) as api: 

1371 event_id = api.CreateEvent( 

1372 events_pb2.CreateEventReq( 

1373 title="Dummy Title", 

1374 content="Dummy content.", 

1375 offline_information=events_pb2.OfflineEventInformation( 

1376 address="Near Null Island", 

1377 lat=0.1, 

1378 lng=0.2, 

1379 ), 

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

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

1382 timezone="UTC", 

1383 ) 

1384 ).event_id 

1385 

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

1387 with events_session(token) as api: 

1388 api.SetEventAttendance( 

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

1390 ) 

1391 

1392 with events_session(token6) as api: 

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

1394 

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

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

1397 

1398 res = api.ListEventAttendees( 

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

1400 ) 

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

1402 

1403 res = api.ListEventAttendees( 

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

1405 ) 

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

1407 assert not res.next_page_token 

1408 

1409 

1410def test_ListEventSubscribers(db): 

1411 # event creator 

1412 user1, token1 = generate_user() 

1413 # others 

1414 user2, token2 = generate_user() 

1415 user3, token3 = generate_user() 

1416 user4, token4 = generate_user() 

1417 user5, token5 = generate_user() 

1418 user6, token6 = generate_user() 

1419 

1420 with session_scope() as session: 

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

1422 

1423 with events_session(token1) as api: 

1424 event_id = api.CreateEvent( 

1425 events_pb2.CreateEventReq( 

1426 title="Dummy Title", 

1427 content="Dummy content.", 

1428 offline_information=events_pb2.OfflineEventInformation( 

1429 address="Near Null Island", 

1430 lat=0.1, 

1431 lng=0.2, 

1432 ), 

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

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

1435 timezone="UTC", 

1436 ) 

1437 ).event_id 

1438 

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

1440 with events_session(token) as api: 

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

1442 

1443 with events_session(token6) as api: 

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

1445 

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

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

1448 

1449 res = api.ListEventSubscribers( 

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

1451 ) 

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

1453 

1454 res = api.ListEventSubscribers( 

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

1456 ) 

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

1458 assert not res.next_page_token 

1459 

1460 

1461def test_ListEventOrganizers(db): 

1462 # event creator 

1463 user1, token1 = generate_user() 

1464 # others 

1465 user2, token2 = generate_user() 

1466 user3, token3 = generate_user() 

1467 user4, token4 = generate_user() 

1468 user5, token5 = generate_user() 

1469 user6, token6 = generate_user() 

1470 

1471 with session_scope() as session: 

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

1473 

1474 with events_session(token1) as api: 

1475 event_id = api.CreateEvent( 

1476 events_pb2.CreateEventReq( 

1477 title="Dummy Title", 

1478 content="Dummy content.", 

1479 offline_information=events_pb2.OfflineEventInformation( 

1480 address="Near Null Island", 

1481 lat=0.1, 

1482 lng=0.2, 

1483 ), 

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

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

1486 timezone="UTC", 

1487 ) 

1488 ).event_id 

1489 

1490 with events_session(token1) as api: 

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

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

1493 

1494 with events_session(token6) as api: 

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

1496 

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

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

1499 

1500 res = api.ListEventOrganizers( 

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

1502 ) 

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

1504 

1505 res = api.ListEventOrganizers( 

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

1507 ) 

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

1509 assert not res.next_page_token 

1510 

1511 

1512def test_TransferEvent(db): 

1513 user1, token1 = generate_user() 

1514 user2, token2 = generate_user() 

1515 user3, token3 = generate_user() 

1516 user4, token4 = generate_user() 

1517 

1518 with session_scope() as session: 

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

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

1521 c_id = c.id 

1522 h_id = h.id 

1523 

1524 with events_session(token1) as api: 

1525 event_id = api.CreateEvent( 

1526 events_pb2.CreateEventReq( 

1527 title="Dummy Title", 

1528 content="Dummy content.", 

1529 offline_information=events_pb2.OfflineEventInformation( 

1530 address="Near Null Island", 

1531 lat=0.1, 

1532 lng=0.2, 

1533 ), 

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

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

1536 timezone="UTC", 

1537 ) 

1538 ).event_id 

1539 

1540 api.TransferEvent( 

1541 events_pb2.TransferEventReq( 

1542 event_id=event_id, 

1543 new_owner_community_id=c_id, 

1544 ) 

1545 ) 

1546 

1547 # remove ourselves as organizer, otherwise we can still edit it 

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

1549 

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

1551 api.TransferEvent( 

1552 events_pb2.TransferEventReq( 

1553 event_id=event_id, 

1554 new_owner_group_id=h_id, 

1555 ) 

1556 ) 

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

1558 assert e.value.details() == "You're not allowed to transfer that event." 

1559 

1560 event_id = api.CreateEvent( 

1561 events_pb2.CreateEventReq( 

1562 title="Dummy Title", 

1563 content="Dummy content.", 

1564 offline_information=events_pb2.OfflineEventInformation( 

1565 address="Near Null Island", 

1566 lat=0.1, 

1567 lng=0.2, 

1568 ), 

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

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

1571 timezone="UTC", 

1572 ) 

1573 ).event_id 

1574 

1575 api.TransferEvent( 

1576 events_pb2.TransferEventReq( 

1577 event_id=event_id, 

1578 new_owner_group_id=h_id, 

1579 ) 

1580 ) 

1581 

1582 # remove ourselves as organizer, otherwise we can still edit it 

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

1584 

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

1586 api.TransferEvent( 

1587 events_pb2.TransferEventReq( 

1588 event_id=event_id, 

1589 new_owner_community_id=c_id, 

1590 ) 

1591 ) 

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

1593 assert e.value.details() == "You're not allowed to transfer that event." 

1594 

1595 

1596def test_SetEventSubscription(db): 

1597 user1, token1 = generate_user() 

1598 user2, token2 = generate_user() 

1599 

1600 with session_scope() as session: 

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

1602 

1603 with events_session(token1) as api: 

1604 event_id = api.CreateEvent( 

1605 events_pb2.CreateEventReq( 

1606 title="Dummy Title", 

1607 content="Dummy content.", 

1608 offline_information=events_pb2.OfflineEventInformation( 

1609 address="Near Null Island", 

1610 lat=0.1, 

1611 lng=0.2, 

1612 ), 

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

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

1615 timezone="UTC", 

1616 ) 

1617 ).event_id 

1618 

1619 with events_session(token2) as api: 

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

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

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

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

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

1625 

1626 

1627def test_SetEventAttendance(db): 

1628 user1, token1 = generate_user() 

1629 user2, token2 = generate_user() 

1630 

1631 with session_scope() as session: 

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

1633 

1634 with events_session(token1) as api: 

1635 event_id = api.CreateEvent( 

1636 events_pb2.CreateEventReq( 

1637 title="Dummy Title", 

1638 content="Dummy content.", 

1639 offline_information=events_pb2.OfflineEventInformation( 

1640 address="Near Null Island", 

1641 lat=0.1, 

1642 lng=0.2, 

1643 ), 

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

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

1646 timezone="UTC", 

1647 ) 

1648 ).event_id 

1649 

1650 with events_session(token2) as api: 

1651 assert ( 

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

1653 == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1654 ) 

1655 api.SetEventAttendance( 

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

1657 ) 

1658 assert ( 

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

1660 == events_pb2.ATTENDANCE_STATE_GOING 

1661 ) 

1662 api.SetEventAttendance( 

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

1664 ) 

1665 assert ( 

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

1667 == events_pb2.ATTENDANCE_STATE_MAYBE 

1668 ) 

1669 api.SetEventAttendance( 

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

1671 ) 

1672 assert ( 

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

1674 == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1675 ) 

1676 

1677 

1678def test_InviteEventOrganizer(db): 

1679 user1, token1 = generate_user() 

1680 user2, token2 = generate_user() 

1681 

1682 with session_scope() as session: 

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

1684 

1685 with events_session(token1) as api: 

1686 event_id = api.CreateEvent( 

1687 events_pb2.CreateEventReq( 

1688 title="Dummy Title", 

1689 content="Dummy content.", 

1690 offline_information=events_pb2.OfflineEventInformation( 

1691 address="Near Null Island", 

1692 lat=0.1, 

1693 lng=0.2, 

1694 ), 

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

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

1697 timezone="UTC", 

1698 ) 

1699 ).event_id 

1700 

1701 with events_session(token2) as api: 

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

1703 

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

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

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

1707 assert e.value.details() == "You're not allowed to edit that event." 

1708 

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

1710 

1711 with events_session(token1) as api: 

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

1713 

1714 with events_session(token2) as api: 

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

1716 

1717 

1718def test_ListEventOccurrences(db): 

1719 user1, token1 = generate_user() 

1720 user2, token2 = generate_user() 

1721 user3, token3 = generate_user() 

1722 

1723 with session_scope() as session: 

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

1725 

1726 start = now() 

1727 

1728 event_ids = [] 

1729 

1730 with events_session(token1) as api: 

1731 res = api.CreateEvent( 

1732 events_pb2.CreateEventReq( 

1733 title="First occurrence", 

1734 content="Dummy content.", 

1735 parent_community_id=c_id, 

1736 online_information=events_pb2.OnlineEventInformation( 

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

1738 ), 

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

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

1741 timezone="UTC", 

1742 ) 

1743 ) 

1744 

1745 event_ids.append(res.event_id) 

1746 

1747 for i in range(5): 

1748 res = api.ScheduleEvent( 

1749 events_pb2.ScheduleEventReq( 

1750 event_id=event_ids[-1], 

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

1752 online_information=events_pb2.OnlineEventInformation( 

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

1754 ), 

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

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

1757 timezone="UTC", 

1758 ) 

1759 ) 

1760 

1761 event_ids.append(res.event_id) 

1762 

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

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

1765 

1766 res = api.ListEventOccurrences( 

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

1768 ) 

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

1770 

1771 res = api.ListEventOccurrences( 

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

1773 ) 

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

1775 assert not res.next_page_token 

1776 

1777 

1778def test_ListMyEvents(db): 

1779 user1, token1 = generate_user() 

1780 user2, token2 = generate_user() 

1781 user3, token3 = generate_user() 

1782 user4, token4 = generate_user() 

1783 user5, token5 = generate_user() 

1784 

1785 with session_scope() as session: 

1786 # Create global community first (node_id=1), then a child community (node_id=2) 

1787 # This allows testing my_communities_exclude_global 

1788 global_community = create_community(session, 0, 100, "Global", [user3], [], None) 

1789 c_id = global_community.id 

1790 child_community = create_community(session, 0, 50, "Child Community", [user3, user4], [], global_community) 

1791 c2_id = child_community.id 

1792 

1793 start = now() 

1794 

1795 def new_event(hours_from_now: int, community_id: int, online: bool = True) -> events_pb2.CreateEventReq: 

1796 if online: 

1797 return events_pb2.CreateEventReq( 

1798 title="Dummy Online Title", 

1799 content="Dummy content.", 

1800 online_information=events_pb2.OnlineEventInformation( 

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

1802 ), 

1803 parent_community_id=community_id, 

1804 timezone="UTC", 

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

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

1807 ) 

1808 else: 

1809 return events_pb2.CreateEventReq( 

1810 title="Dummy Offline Title", 

1811 content="Dummy content.", 

1812 offline_information=events_pb2.OfflineEventInformation( 

1813 address="Near Null Island", 

1814 lat=0.1, 

1815 lng=0.2, 

1816 ), 

1817 parent_community_id=community_id, 

1818 timezone="UTC", 

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

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

1821 ) 

1822 

1823 with events_session(token1) as api: 

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

1825 

1826 with events_session(token2) as api: 

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

1828 

1829 with events_session(token1) as api: 

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

1831 

1832 with events_session(token2) as api: 

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

1834 

1835 with events_session(token3) as api: 

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

1837 

1838 with events_session(token4) as api: 

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

1840 

1841 with events_session(token1) as api: 

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

1843 

1844 with events_session(token1) as api: 

1845 api.SetEventAttendance( 

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

1847 ) 

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

1849 

1850 with events_session(token2) as api: 

1851 api.SetEventAttendance( 

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

1853 ) 

1854 

1855 with events_session(token3) as api: 

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

1857 

1858 with events_session(token1) as api: 

1859 # test pagination with token first 

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

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

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

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

1864 assert not res.next_page_token 

1865 

1866 res = api.ListMyEvents( 

1867 events_pb2.ListMyEventsReq( 

1868 subscribed=True, 

1869 attending=True, 

1870 organizing=True, 

1871 ) 

1872 ) 

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

1874 

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

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

1877 

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

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

1880 

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

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

1883 

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

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

1886 

1887 with events_session(token1) as api: 

1888 # Test pagination with page_number and verify total_items 

1889 res = api.ListMyEvents( 

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

1891 ) 

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

1893 assert res.total_items == 4 

1894 

1895 res = api.ListMyEvents( 

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

1897 ) 

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

1899 assert res.total_items == 4 

1900 

1901 # Verify no more pages 

1902 res = api.ListMyEvents( 

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

1904 ) 

1905 assert not res.events 

1906 assert res.total_items == 4 

1907 

1908 with events_session(token2) as api: 

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

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

1911 

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

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

1914 

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

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

1917 

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

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

1920 

1921 with events_session(token3) as api: 

1922 # user3 is member of both global (c_id) and child (c2_id) communities 

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

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

1925 

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

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

1928 

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

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

1931 

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

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

1934 

1935 # my_communities returns events from both communities user3 is a member of 

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

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

1938 

1939 # my_communities_exclude_global filters out events from global community (node_id=1) 

1940 res = api.ListMyEvents(events_pb2.ListMyEventsReq(my_communities=True, my_communities_exclude_global=True)) 

1941 assert [event.event_id for event in res.events] == [e6] 

1942 

1943 # my_communities_exclude_global works independently of my_communities flag 

1944 res = api.ListMyEvents(events_pb2.ListMyEventsReq(my_communities_exclude_global=True)) 

1945 assert [event.event_id for event in res.events] == [e6] 

1946 

1947 # my_communities_exclude_global filters organizing results too 

1948 res = api.ListMyEvents(events_pb2.ListMyEventsReq(organizing=True, my_communities_exclude_global=True)) 

1949 assert [event.event_id for event in res.events] == [] 

1950 

1951 # my_communities_exclude_global filters subscribed results too 

1952 res = api.ListMyEvents(events_pb2.ListMyEventsReq(subscribed=True, my_communities_exclude_global=True)) 

1953 assert [event.event_id for event in res.events] == [] 

1954 

1955 with events_session(token5) as api: 

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

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

1958 

1959 

1960def test_RemoveEventOrganizer(db): 

1961 user1, token1 = generate_user() 

1962 user2, token2 = generate_user() 

1963 

1964 with session_scope() as session: 

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

1966 

1967 with events_session(token1) as api: 

1968 event_id = api.CreateEvent( 

1969 events_pb2.CreateEventReq( 

1970 title="Dummy Title", 

1971 content="Dummy content.", 

1972 offline_information=events_pb2.OfflineEventInformation( 

1973 address="Near Null Island", 

1974 lat=0.1, 

1975 lng=0.2, 

1976 ), 

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

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

1979 timezone="UTC", 

1980 ) 

1981 ).event_id 

1982 

1983 with events_session(token2) as api: 

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

1985 

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

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

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

1989 assert e.value.details() == "You're not allowed to edit that event." 

1990 

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

1992 

1993 with events_session(token1) as api: 

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

1995 

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

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

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

1999 assert e.value.details() == "You cannot remove the event owner as an organizer." 

2000 

2001 with events_session(token2) as api: 

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

2003 assert res.organizer 

2004 assert res.organizer_count == 2 

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

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

2007 

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

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

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

2011 assert e.value.details() == "You're not allowed to edit that event." 

2012 

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

2014 assert not res.organizer 

2015 assert res.organizer_count == 1 

2016 

2017 # Test that event owner can remove co-organizers 

2018 with events_session(token1) as api: 

2019 # Add user2 back as organizer 

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

2021 

2022 # Verify user2 is now an organizer 

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

2024 assert res.organizer_count == 2 

2025 

2026 # Event owner can remove co-organizer 

2027 api.RemoveEventOrganizer( 

2028 events_pb2.RemoveEventOrganizerReq(event_id=event_id, user_id=wrappers_pb2.Int64Value(value=user2.id)) 

2029 ) 

2030 

2031 # Verify user2 is no longer an organizer 

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

2033 assert res.organizer_count == 1 

2034 

2035 # Test that non-organizers cannot remove other organizers 

2036 with events_session(token2) as api: 

2037 # User2 cannot invite themselves as organizer (not the owner) 

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

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

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

2041 assert e.value.details() == "You're not allowed to edit that event." 

2042 

2043 # Test that non-organizers cannot remove other organizers (user1 adds user2 back first) 

2044 with events_session(token1) as api: 

2045 # Add user2 back as organizer 

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

2047 

2048 

2049def test_ListEventAttendees_regression(db): 

2050 # see issue #1617: 

2051 # 

2052 # 1. Create an event 

2053 # 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` 

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

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

2056 # 

2057 # **Expected behaviour** 

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

2059 # 

2060 # **Actual/current behaviour** 

2061 # `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 

2062 

2063 user1, token1 = generate_user() 

2064 user2, token2 = generate_user() 

2065 user3, token3 = generate_user() 

2066 user4, token4 = generate_user() 

2067 user5, token5 = generate_user() 

2068 

2069 with session_scope() as session: 

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

2071 

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

2073 end_time = start_time + timedelta(hours=3) 

2074 

2075 with events_session(token1) as api: 

2076 res = api.CreateEvent( 

2077 events_pb2.CreateEventReq( 

2078 title="Dummy Title", 

2079 content="Dummy content.", 

2080 online_information=events_pb2.OnlineEventInformation( 

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

2082 ), 

2083 parent_community_id=c_id, 

2084 start_time=Timestamp_from_datetime(start_time), 

2085 end_time=Timestamp_from_datetime(end_time), 

2086 timezone="UTC", 

2087 ) 

2088 ) 

2089 

2090 res = api.TransferEvent( 

2091 events_pb2.TransferEventReq( 

2092 event_id=res.event_id, 

2093 new_owner_community_id=c_id, 

2094 ) 

2095 ) 

2096 

2097 event_id = res.event_id 

2098 

2099 api.SetEventAttendance( 

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

2101 ) 

2102 api.SetEventAttendance( 

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

2104 ) 

2105 

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

2107 assert len(res.attendee_user_ids) == 1 

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

2109 

2110 

2111def test_event_threads(db, push_collector: PushCollector): 

2112 user1, token1 = generate_user() 

2113 user2, token2 = generate_user() 

2114 user3, token3 = generate_user() 

2115 user4, token4 = generate_user() 

2116 

2117 with session_scope() as session: 

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

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

2120 c_id = c.id 

2121 h_id = h.id 

2122 user4_id = user4.id 

2123 

2124 with events_session(token1) as api: 

2125 event = api.CreateEvent( 

2126 events_pb2.CreateEventReq( 

2127 title="Dummy Title", 

2128 content="Dummy content.", 

2129 offline_information=events_pb2.OfflineEventInformation( 

2130 address="Near Null Island", 

2131 lat=0.1, 

2132 lng=0.2, 

2133 ), 

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

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

2136 timezone="UTC", 

2137 ) 

2138 ) 

2139 

2140 with threads_session(token2) as api: 

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

2142 

2143 with events_session(token3) as api: 

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

2145 assert res.thread.num_responses == 1 

2146 

2147 with threads_session(token3) as api: 

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

2149 assert len(ret.replies) == 1 

2150 assert not ret.next_page_token 

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

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

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

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

2155 

2156 api.PostReply(threads_pb2.PostReplyReq(thread_id=reply_id, content="what a silly comment")) 

2157 

2158 process_jobs() 

2159 

2160 assert push_collector.pop_for_user(user1.id, last=True).content.title == f"{user2.name} • Dummy Title" 

2161 assert push_collector.pop_for_user(user2.id, last=True).content.title == f"{user3.name} • Dummy Title" 

2162 assert push_collector.count_for_user(user4_id) == 0 

2163 

2164 

2165def test_can_overlap_other_events_schedule_regression(db): 

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

2167 user, token = generate_user() 

2168 

2169 with session_scope() as session: 

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

2171 

2172 start = now() 

2173 

2174 with events_session(token) as api: 

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

2176 api.CreateEvent( 

2177 events_pb2.CreateEventReq( 

2178 title="Dummy Title", 

2179 content="Dummy content.", 

2180 parent_community_id=c_id, 

2181 online_information=events_pb2.OnlineEventInformation( 

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

2183 ), 

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

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

2186 timezone="UTC", 

2187 ) 

2188 ) 

2189 

2190 # this event 

2191 res = api.CreateEvent( 

2192 events_pb2.CreateEventReq( 

2193 title="Dummy Title", 

2194 content="Dummy content.", 

2195 parent_community_id=c_id, 

2196 online_information=events_pb2.OnlineEventInformation( 

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

2198 ), 

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

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

2201 timezone="UTC", 

2202 ) 

2203 ) 

2204 

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

2206 api.ScheduleEvent( 

2207 events_pb2.ScheduleEventReq( 

2208 event_id=res.event_id, 

2209 content="New event occurrence", 

2210 offline_information=events_pb2.OfflineEventInformation( 

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

2212 lat=0.3, 

2213 lng=0.2, 

2214 ), 

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

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

2217 timezone="UTC", 

2218 ) 

2219 ) 

2220 

2221 

2222def test_can_overlap_other_events_update_regression(db): 

2223 user, token = generate_user() 

2224 

2225 with session_scope() as session: 

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

2227 

2228 start = now() 

2229 

2230 with events_session(token) as api: 

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

2232 api.CreateEvent( 

2233 events_pb2.CreateEventReq( 

2234 title="Dummy Title", 

2235 content="Dummy content.", 

2236 parent_community_id=c_id, 

2237 online_information=events_pb2.OnlineEventInformation( 

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

2239 ), 

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

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

2242 timezone="UTC", 

2243 ) 

2244 ) 

2245 

2246 res = api.CreateEvent( 

2247 events_pb2.CreateEventReq( 

2248 title="Dummy Title", 

2249 content="Dummy content.", 

2250 parent_community_id=c_id, 

2251 online_information=events_pb2.OnlineEventInformation( 

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

2253 ), 

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

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

2256 timezone="UTC", 

2257 ) 

2258 ) 

2259 

2260 event_id = api.ScheduleEvent( 

2261 events_pb2.ScheduleEventReq( 

2262 event_id=res.event_id, 

2263 content="New event occurrence", 

2264 offline_information=events_pb2.OfflineEventInformation( 

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

2266 lat=0.3, 

2267 lng=0.2, 

2268 ), 

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

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

2271 timezone="UTC", 

2272 ) 

2273 ).event_id 

2274 

2275 # can overlap with this current existing occurrence 

2276 api.UpdateEvent( 

2277 events_pb2.UpdateEventReq( 

2278 event_id=event_id, 

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

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

2281 ) 

2282 ) 

2283 

2284 api.UpdateEvent( 

2285 events_pb2.UpdateEventReq( 

2286 event_id=event_id, 

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

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

2289 ) 

2290 ) 

2291 

2292 

2293def test_list_past_events_regression(db): 

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

2295 user, token = generate_user() 

2296 

2297 with session_scope() as session: 

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

2299 

2300 start = now() 

2301 

2302 with events_session(token) as api: 

2303 api.CreateEvent( 

2304 events_pb2.CreateEventReq( 

2305 title="Dummy Title", 

2306 content="Dummy content.", 

2307 parent_community_id=c_id, 

2308 online_information=events_pb2.OnlineEventInformation( 

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

2310 ), 

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

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

2313 timezone="UTC", 

2314 ) 

2315 ) 

2316 

2317 with session_scope() as session: 

2318 session.execute( 

2319 update(EventOccurrence).values( 

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

2321 ) 

2322 ) 

2323 

2324 with events_session(token) as api: 

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

2326 assert len(res.events) == 1 

2327 

2328 

2329def test_community_invite_requests(db): 

2330 user1, token1 = generate_user(complete_profile=True) 

2331 user2, token2 = generate_user() 

2332 user3, token3 = generate_user() 

2333 user4, token4 = generate_user() 

2334 user5, token5 = generate_user(is_superuser=True) 

2335 

2336 with session_scope() as session: 

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

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

2339 

2340 enforce_community_memberships() 

2341 

2342 with events_session(token1) as api: 

2343 res = api.CreateEvent( 

2344 events_pb2.CreateEventReq( 

2345 title="Dummy Title", 

2346 content="Dummy content.", 

2347 parent_community_id=c_id, 

2348 online_information=events_pb2.OnlineEventInformation( 

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

2350 ), 

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

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

2353 timezone="UTC", 

2354 ) 

2355 ) 

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

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

2358 

2359 event_id = res.event_id 

2360 

2361 with mock_notification_email() as mock: 

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

2363 assert mock.call_count == 1 

2364 e = email_fields(mock) 

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

2366 

2367 assert user_url in e.plain 

2368 assert event_url in e.plain 

2369 

2370 # can't send another req 

2371 with pytest.raises(grpc.RpcError) as err: 

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

2373 assert err.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

2374 assert err.value.details() == "You have already requested a community invite for this event." 

2375 

2376 # another user can send one though 

2377 with events_session(token3) as api: 

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

2379 

2380 # but not a non-admin 

2381 with events_session(token2) as api: 

2382 with pytest.raises(grpc.RpcError) as err: 

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

2384 assert err.value.code() == grpc.StatusCode.PERMISSION_DENIED 

2385 assert err.value.details() == "You're not allowed to edit that event." 

2386 

2387 with real_editor_session(token5) as editor: 

2388 res = editor.ListEventCommunityInviteRequests(editor_pb2.ListEventCommunityInviteRequestsReq()) 

2389 assert len(res.requests) == 2 

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

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

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

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

2394 

2395 editor.DecideEventCommunityInviteRequest( 

2396 editor_pb2.DecideEventCommunityInviteRequestReq( 

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

2398 approve=False, 

2399 ) 

2400 ) 

2401 

2402 editor.DecideEventCommunityInviteRequest( 

2403 editor_pb2.DecideEventCommunityInviteRequestReq( 

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

2405 approve=True, 

2406 ) 

2407 ) 

2408 

2409 # not after approve 

2410 with events_session(token4) as api: 

2411 with pytest.raises(grpc.RpcError) as err: 

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

2413 assert err.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

2414 assert err.value.details() == "A community invite has already been sent out for this event." 

2415 

2416 

2417def test_update_event_should_notify_queues_job(): 

2418 user, token = generate_user() 

2419 start = now() 

2420 

2421 with session_scope() as session: 

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

2423 

2424 # create an event 

2425 with events_session(token) as api: 

2426 create_res = api.CreateEvent( 

2427 events_pb2.CreateEventReq( 

2428 title="Dummy Title", 

2429 content="Dummy content.", 

2430 parent_community_id=c_id, 

2431 offline_information=events_pb2.OfflineEventInformation( 

2432 address="https://couchers.org/meet/", 

2433 lat=1.0, 

2434 lng=2.0, 

2435 ), 

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

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

2438 timezone="UTC", 

2439 ) 

2440 ) 

2441 

2442 event_id = create_res.event_id 

2443 

2444 # measure initial background job queue length 

2445 with session_scope() as session: 

2446 jobs = session.query(BackgroundJob).all() 

2447 job_length_before_update = len(jobs) 

2448 

2449 # update with should_notify=False, expect no change in background job queue 

2450 api.UpdateEvent( 

2451 events_pb2.UpdateEventReq( 

2452 event_id=event_id, 

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

2454 should_notify=False, 

2455 ) 

2456 ) 

2457 

2458 with session_scope() as session: 

2459 jobs = session.query(BackgroundJob).all() 

2460 assert len(jobs) == job_length_before_update 

2461 

2462 # update with should_notify=True, expect one new background job added 

2463 api.UpdateEvent( 

2464 events_pb2.UpdateEventReq( 

2465 event_id=event_id, 

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

2467 should_notify=True, 

2468 ) 

2469 ) 

2470 

2471 with session_scope() as session: 

2472 jobs = session.query(BackgroundJob).all() 

2473 assert len(jobs) == job_length_before_update + 1 

2474 

2475 

2476def test_event_photo_key(db): 

2477 """Test that events return the photo_key field when a photo is set.""" 

2478 user, token = generate_user() 

2479 

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

2481 end_time = start_time + timedelta(hours=3) 

2482 

2483 # Create a community and an upload for the event photo 

2484 with session_scope() as session: 

2485 create_community(session, 0, 2, "Community", [user], [], None) 

2486 upload = Upload( 

2487 key="test_event_photo_key_123", 

2488 filename="test_event_photo_key_123.jpg", 

2489 creator_user_id=user.id, 

2490 ) 

2491 session.add(upload) 

2492 

2493 with events_session(token) as api: 

2494 # Create event without photo 

2495 res = api.CreateEvent( 

2496 events_pb2.CreateEventReq( 

2497 title="Event Without Photo", 

2498 content="No photo content.", 

2499 photo_key=None, 

2500 offline_information=events_pb2.OfflineEventInformation( 

2501 address="Near Null Island", 

2502 lat=0.1, 

2503 lng=0.2, 

2504 ), 

2505 start_time=Timestamp_from_datetime(start_time), 

2506 end_time=Timestamp_from_datetime(end_time), 

2507 timezone="UTC", 

2508 ) 

2509 ) 

2510 

2511 assert res.photo_key == "" 

2512 assert res.photo_url == "" 

2513 

2514 # Create event with photo 

2515 res_with_photo = api.CreateEvent( 

2516 events_pb2.CreateEventReq( 

2517 title="Event With Photo", 

2518 content="Has photo content.", 

2519 photo_key="test_event_photo_key_123", 

2520 offline_information=events_pb2.OfflineEventInformation( 

2521 address="Near Null Island", 

2522 lat=0.1, 

2523 lng=0.2, 

2524 ), 

2525 start_time=Timestamp_from_datetime(start_time + timedelta(days=1)), 

2526 end_time=Timestamp_from_datetime(end_time + timedelta(days=1)), 

2527 timezone="UTC", 

2528 ) 

2529 ) 

2530 

2531 assert res_with_photo.photo_key == "test_event_photo_key_123" 

2532 assert "test_event_photo_key_123" in res_with_photo.photo_url 

2533 

2534 event_id = res_with_photo.event_id 

2535 

2536 # Verify photo_key is returned when getting the event 

2537 get_res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id)) 

2538 assert get_res.photo_key == "test_event_photo_key_123" 

2539 assert "test_event_photo_key_123" in get_res.photo_url