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

1137 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-07-22 17:19 +0000

1from datetime import timedelta 

2 

3import grpc 

4import pytest 

5from google.protobuf import wrappers_pb2 

6from psycopg2.extras import DateTimeTZRange 

7from sqlalchemy.sql.expression import update 

8 

9from couchers import errors 

10from couchers.db import session_scope 

11from couchers.models import EventOccurrence 

12from couchers.tasks import enforce_community_memberships 

13from couchers.utils import Timestamp_from_datetime, now, to_aware_datetime 

14from proto import admin_pb2, events_pb2, threads_pb2 

15from tests.test_communities import create_community, create_group 

16from tests.test_fixtures import ( # noqa 

17 db, 

18 email_fields, 

19 events_session, 

20 generate_user, 

21 mock_notification_email, 

22 real_admin_session, 

23 testconfig, 

24 threads_session, 

25) 

26 

27 

28@pytest.fixture(autouse=True) 

29def _(testconfig): 

30 pass 

31 

32 

33def test_CreateEvent(db): 

34 # test cases: 

35 # can create event 

36 # cannot create event with missing details 

37 # can create online event 

38 # can create in person event 

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

40 # can create in different timezones 

41 

42 # event creator 

43 user1, token1 = generate_user() 

44 # community moderator 

45 user2, token2 = generate_user() 

46 # third party 

47 user3, token3 = generate_user() 

48 

49 with session_scope() as session: 

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

51 

52 time_before = now() 

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

54 end_time = start_time + timedelta(hours=3) 

55 

56 with events_session(token1) as api: 

57 # in person event 

58 res = api.CreateEvent( 

59 events_pb2.CreateEventReq( 

60 title="Dummy Title", 

61 content="Dummy content.", 

62 photo_key=None, 

63 offline_information=events_pb2.OfflineEventInformation( 

64 address="Near Null Island", 

65 lat=0.1, 

66 lng=0.2, 

67 ), 

68 start_time=Timestamp_from_datetime(start_time), 

69 end_time=Timestamp_from_datetime(end_time), 

70 timezone="UTC", 

71 ) 

72 ) 

73 

74 assert res.is_next 

75 assert res.title == "Dummy Title" 

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

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

78 assert not res.photo_url 

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

80 assert res.offline_information.lat == 0.1 

81 assert res.offline_information.lng == 0.2 

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

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

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

85 assert res.creator_user_id == user1.id 

86 assert to_aware_datetime(res.start_time) == start_time 

87 assert to_aware_datetime(res.end_time) == end_time 

88 # assert res.timezone == "UTC" 

89 assert res.start_time_display 

90 assert res.end_time_display 

91 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

92 assert res.organizer 

93 assert res.subscriber 

94 assert res.going_count == 1 

95 assert res.maybe_count == 0 

96 assert res.organizer_count == 1 

97 assert res.subscriber_count == 1 

98 assert res.owner_user_id == user1.id 

99 assert not res.owner_community_id 

100 assert not res.owner_group_id 

101 assert res.thread.thread_id 

102 assert res.can_edit 

103 assert not res.can_moderate 

104 

105 event_id = res.event_id 

106 

107 with events_session(token2) as api: 

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

109 

110 assert res.is_next 

111 assert res.title == "Dummy Title" 

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

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

114 assert not res.photo_url 

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

116 assert res.offline_information.lat == 0.1 

117 assert res.offline_information.lng == 0.2 

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

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

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

121 assert res.creator_user_id == user1.id 

122 assert to_aware_datetime(res.start_time) == start_time 

123 assert to_aware_datetime(res.end_time) == end_time 

124 # assert res.timezone == "UTC" 

125 assert res.start_time_display 

126 assert res.end_time_display 

127 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

128 assert not res.organizer 

129 assert not res.subscriber 

130 assert res.going_count == 1 

131 assert res.maybe_count == 0 

132 assert res.organizer_count == 1 

133 assert res.subscriber_count == 1 

134 assert res.owner_user_id == user1.id 

135 assert not res.owner_community_id 

136 assert not res.owner_group_id 

137 assert res.thread.thread_id 

138 assert res.can_edit 

139 assert res.can_moderate 

140 

141 with events_session(token3) as api: 

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

143 

144 assert res.is_next 

145 assert res.title == "Dummy Title" 

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

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

148 assert not res.photo_url 

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

150 assert res.offline_information.lat == 0.1 

151 assert res.offline_information.lng == 0.2 

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

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

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

155 assert res.creator_user_id == user1.id 

156 assert to_aware_datetime(res.start_time) == start_time 

157 assert to_aware_datetime(res.end_time) == end_time 

158 # assert res.timezone == "UTC" 

159 assert res.start_time_display 

160 assert res.end_time_display 

161 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

162 assert not res.organizer 

163 assert not res.subscriber 

164 assert res.going_count == 1 

165 assert res.maybe_count == 0 

166 assert res.organizer_count == 1 

167 assert res.subscriber_count == 1 

168 assert res.owner_user_id == user1.id 

169 assert not res.owner_community_id 

170 assert not res.owner_group_id 

171 assert res.thread.thread_id 

172 assert not res.can_edit 

173 assert not res.can_moderate 

174 

175 with events_session(token1) as api: 

176 # online only event 

177 res = api.CreateEvent( 

178 events_pb2.CreateEventReq( 

179 title="Dummy Title", 

180 content="Dummy content.", 

181 photo_key=None, 

182 online_information=events_pb2.OnlineEventInformation( 

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

184 ), 

185 parent_community_id=c_id, 

186 start_time=Timestamp_from_datetime(start_time), 

187 end_time=Timestamp_from_datetime(end_time), 

188 timezone="UTC", 

189 ) 

190 ) 

191 

192 assert res.is_next 

193 assert res.title == "Dummy Title" 

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

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

196 assert not res.photo_url 

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

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

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

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

201 assert res.creator_user_id == user1.id 

202 assert to_aware_datetime(res.start_time) == start_time 

203 assert to_aware_datetime(res.end_time) == end_time 

204 # assert res.timezone == "UTC" 

205 assert res.start_time_display 

206 assert res.end_time_display 

207 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

208 assert res.organizer 

209 assert res.subscriber 

210 assert res.going_count == 1 

211 assert res.maybe_count == 0 

212 assert res.organizer_count == 1 

213 assert res.subscriber_count == 1 

214 assert res.owner_user_id == user1.id 

215 assert not res.owner_community_id 

216 assert not res.owner_group_id 

217 assert res.thread.thread_id 

218 assert res.can_edit 

219 assert not res.can_moderate 

220 

221 event_id = res.event_id 

222 

223 with events_session(token2) as api: 

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

225 

226 assert res.is_next 

227 assert res.title == "Dummy Title" 

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

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

230 assert not res.photo_url 

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

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

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

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

235 assert res.creator_user_id == user1.id 

236 assert to_aware_datetime(res.start_time) == start_time 

237 assert to_aware_datetime(res.end_time) == end_time 

238 # assert res.timezone == "UTC" 

239 assert res.start_time_display 

240 assert res.end_time_display 

241 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

242 assert not res.organizer 

243 assert not res.subscriber 

244 assert res.going_count == 1 

245 assert res.maybe_count == 0 

246 assert res.organizer_count == 1 

247 assert res.subscriber_count == 1 

248 assert res.owner_user_id == user1.id 

249 assert not res.owner_community_id 

250 assert not res.owner_group_id 

251 assert res.thread.thread_id 

252 assert res.can_edit 

253 assert res.can_moderate 

254 

255 with events_session(token3) as api: 

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

257 

258 assert res.is_next 

259 assert res.title == "Dummy Title" 

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

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

262 assert not res.photo_url 

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

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

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

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

267 assert res.creator_user_id == user1.id 

268 assert to_aware_datetime(res.start_time) == start_time 

269 assert to_aware_datetime(res.end_time) == end_time 

270 # assert res.timezone == "UTC" 

271 assert res.start_time_display 

272 assert res.end_time_display 

273 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

274 assert not res.organizer 

275 assert not res.subscriber 

276 assert res.going_count == 1 

277 assert res.maybe_count == 0 

278 assert res.organizer_count == 1 

279 assert res.subscriber_count == 1 

280 assert res.owner_user_id == user1.id 

281 assert not res.owner_community_id 

282 assert not res.owner_group_id 

283 assert res.thread.thread_id 

284 assert not res.can_edit 

285 assert not res.can_moderate 

286 

287 with events_session(token1) as api: 

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

289 api.CreateEvent( 

290 events_pb2.CreateEventReq( 

291 title="Dummy Title", 

292 content="Dummy content.", 

293 photo_key=None, 

294 online_information=events_pb2.OnlineEventInformation( 

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

296 ), 

297 start_time=Timestamp_from_datetime(start_time), 

298 end_time=Timestamp_from_datetime(end_time), 

299 timezone="UTC", 

300 ) 

301 ) 

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

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

304 

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

306 api.CreateEvent( 

307 events_pb2.CreateEventReq( 

308 # title="Dummy Title", 

309 content="Dummy content.", 

310 photo_key=None, 

311 offline_information=events_pb2.OfflineEventInformation( 

312 address="Near Null Island", 

313 lat=0.1, 

314 lng=0.1, 

315 ), 

316 start_time=Timestamp_from_datetime(start_time), 

317 end_time=Timestamp_from_datetime(end_time), 

318 timezone="UTC", 

319 ) 

320 ) 

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

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

323 

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

325 api.CreateEvent( 

326 events_pb2.CreateEventReq( 

327 title="Dummy Title", 

328 # content="Dummy content.", 

329 photo_key=None, 

330 offline_information=events_pb2.OfflineEventInformation( 

331 address="Near Null Island", 

332 lat=0.1, 

333 lng=0.1, 

334 ), 

335 start_time=Timestamp_from_datetime(start_time), 

336 end_time=Timestamp_from_datetime(end_time), 

337 timezone="UTC", 

338 ) 

339 ) 

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

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

342 

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

344 api.CreateEvent( 

345 events_pb2.CreateEventReq( 

346 title="Dummy Title", 

347 content="Dummy content.", 

348 photo_key="nonexistent", 

349 offline_information=events_pb2.OfflineEventInformation( 

350 address="Near Null Island", 

351 lat=0.1, 

352 lng=0.1, 

353 ), 

354 start_time=Timestamp_from_datetime(start_time), 

355 end_time=Timestamp_from_datetime(end_time), 

356 timezone="UTC", 

357 ) 

358 ) 

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

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

361 

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

363 api.CreateEvent( 

364 events_pb2.CreateEventReq( 

365 title="Dummy Title", 

366 content="Dummy content.", 

367 photo_key=None, 

368 offline_information=events_pb2.OfflineEventInformation( 

369 address="Near Null Island", 

370 ), 

371 start_time=Timestamp_from_datetime(start_time), 

372 end_time=Timestamp_from_datetime(end_time), 

373 timezone="UTC", 

374 ) 

375 ) 

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

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

378 

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

380 api.CreateEvent( 

381 events_pb2.CreateEventReq( 

382 title="Dummy Title", 

383 content="Dummy content.", 

384 photo_key=None, 

385 offline_information=events_pb2.OfflineEventInformation( 

386 lat=0.1, 

387 lng=0.1, 

388 ), 

389 start_time=Timestamp_from_datetime(start_time), 

390 end_time=Timestamp_from_datetime(end_time), 

391 timezone="UTC", 

392 ) 

393 ) 

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

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

396 

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

398 api.CreateEvent( 

399 events_pb2.CreateEventReq( 

400 title="Dummy Title", 

401 content="Dummy content.", 

402 photo_key=None, 

403 online_information=events_pb2.OnlineEventInformation(), 

404 start_time=Timestamp_from_datetime(start_time), 

405 end_time=Timestamp_from_datetime(end_time), 

406 timezone="UTC", 

407 ) 

408 ) 

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

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

411 

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

413 api.CreateEvent( 

414 events_pb2.CreateEventReq( 

415 title="Dummy Title", 

416 content="Dummy content.", 

417 parent_community_id=c_id, 

418 online_information=events_pb2.OnlineEventInformation( 

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

420 ), 

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

422 end_time=Timestamp_from_datetime(end_time), 

423 timezone="UTC", 

424 ) 

425 ) 

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

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

428 

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

430 api.CreateEvent( 

431 events_pb2.CreateEventReq( 

432 title="Dummy Title", 

433 content="Dummy content.", 

434 parent_community_id=c_id, 

435 online_information=events_pb2.OnlineEventInformation( 

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

437 ), 

438 start_time=Timestamp_from_datetime(end_time), 

439 end_time=Timestamp_from_datetime(start_time), 

440 timezone="UTC", 

441 ) 

442 ) 

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

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

445 

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

447 api.CreateEvent( 

448 events_pb2.CreateEventReq( 

449 title="Dummy Title", 

450 content="Dummy content.", 

451 parent_community_id=c_id, 

452 online_information=events_pb2.OnlineEventInformation( 

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

454 ), 

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

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

457 timezone="UTC", 

458 ) 

459 ) 

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

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

462 

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

464 api.CreateEvent( 

465 events_pb2.CreateEventReq( 

466 title="Dummy Title", 

467 content="Dummy content.", 

468 parent_community_id=c_id, 

469 online_information=events_pb2.OnlineEventInformation( 

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

471 ), 

472 start_time=Timestamp_from_datetime(start_time), 

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

474 timezone="UTC", 

475 ) 

476 ) 

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

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

479 

480 

481def test_ScheduleEvent(db): 

482 # test cases: 

483 # can schedule a new event occurrence 

484 

485 user, token = generate_user() 

486 

487 with session_scope() as session: 

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

489 

490 time_before = now() 

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

492 end_time = start_time + timedelta(hours=3) 

493 

494 with events_session(token) as api: 

495 res = api.CreateEvent( 

496 events_pb2.CreateEventReq( 

497 title="Dummy Title", 

498 content="Dummy content.", 

499 parent_community_id=c_id, 

500 online_information=events_pb2.OnlineEventInformation( 

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

502 ), 

503 start_time=Timestamp_from_datetime(start_time), 

504 end_time=Timestamp_from_datetime(end_time), 

505 timezone="UTC", 

506 ) 

507 ) 

508 

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

510 new_end_time = new_start_time + timedelta(hours=2) 

511 

512 res = api.ScheduleEvent( 

513 events_pb2.ScheduleEventReq( 

514 event_id=res.event_id, 

515 content="New event occurrence", 

516 offline_information=events_pb2.OfflineEventInformation( 

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

518 lat=0.3, 

519 lng=0.2, 

520 ), 

521 start_time=Timestamp_from_datetime(new_start_time), 

522 end_time=Timestamp_from_datetime(new_end_time), 

523 timezone="UTC", 

524 ) 

525 ) 

526 

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

528 

529 assert not res.is_next 

530 assert res.title == "Dummy Title" 

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

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

533 assert not res.photo_url 

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

535 assert res.offline_information.lat == 0.3 

536 assert res.offline_information.lng == 0.2 

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

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

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

540 assert res.creator_user_id == user.id 

541 assert to_aware_datetime(res.start_time) == new_start_time 

542 assert to_aware_datetime(res.end_time) == new_end_time 

543 # assert res.timezone == "UTC" 

544 assert res.start_time_display 

545 assert res.end_time_display 

546 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

547 assert res.organizer 

548 assert res.subscriber 

549 assert res.going_count == 1 

550 assert res.maybe_count == 0 

551 assert res.organizer_count == 1 

552 assert res.subscriber_count == 1 

553 assert res.owner_user_id == user.id 

554 assert not res.owner_community_id 

555 assert not res.owner_group_id 

556 assert res.thread.thread_id 

557 assert res.can_edit 

558 assert res.can_moderate 

559 

560 

561def test_cannot_overlap_occurrences_schedule(db): 

562 user, token = generate_user() 

563 

564 with session_scope() as session: 

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

566 

567 start = now() 

568 

569 with events_session(token) as api: 

570 res = api.CreateEvent( 

571 events_pb2.CreateEventReq( 

572 title="Dummy Title", 

573 content="Dummy content.", 

574 parent_community_id=c_id, 

575 online_information=events_pb2.OnlineEventInformation( 

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

577 ), 

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

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

580 timezone="UTC", 

581 ) 

582 ) 

583 

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

585 api.ScheduleEvent( 

586 events_pb2.ScheduleEventReq( 

587 event_id=res.event_id, 

588 content="New event occurrence", 

589 offline_information=events_pb2.OfflineEventInformation( 

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

591 lat=0.3, 

592 lng=0.2, 

593 ), 

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

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

596 timezone="UTC", 

597 ) 

598 ) 

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

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

601 

602 

603def test_cannot_overlap_occurrences_update(db): 

604 user, token = generate_user() 

605 

606 with session_scope() as session: 

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

608 

609 start = now() 

610 

611 with events_session(token) as api: 

612 res = api.CreateEvent( 

613 events_pb2.CreateEventReq( 

614 title="Dummy Title", 

615 content="Dummy content.", 

616 parent_community_id=c_id, 

617 online_information=events_pb2.OnlineEventInformation( 

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

619 ), 

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

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

622 timezone="UTC", 

623 ) 

624 ) 

625 

626 event_id = api.ScheduleEvent( 

627 events_pb2.ScheduleEventReq( 

628 event_id=res.event_id, 

629 content="New event occurrence", 

630 offline_information=events_pb2.OfflineEventInformation( 

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

632 lat=0.3, 

633 lng=0.2, 

634 ), 

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

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

637 timezone="UTC", 

638 ) 

639 ).event_id 

640 

641 # can overlap with this current existing occurrence 

642 api.UpdateEvent( 

643 events_pb2.UpdateEventReq( 

644 event_id=event_id, 

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

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

647 ) 

648 ) 

649 

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

651 api.UpdateEvent( 

652 events_pb2.UpdateEventReq( 

653 event_id=event_id, 

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

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

656 ) 

657 ) 

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

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

660 

661 

662def test_UpdateEvent_single(db): 

663 # test cases: 

664 # owner can update 

665 # community owner can update 

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

667 # notifies attendees 

668 

669 # event creator 

670 user1, token1 = generate_user() 

671 # community moderator 

672 user2, token2 = generate_user() 

673 # third parties 

674 user3, token3 = generate_user() 

675 user4, token4 = generate_user() 

676 user5, token5 = generate_user() 

677 user6, token6 = generate_user() 

678 

679 with session_scope() as session: 

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

681 

682 time_before = now() 

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

684 end_time = start_time + timedelta(hours=3) 

685 

686 with events_session(token1) as api: 

687 res = api.CreateEvent( 

688 events_pb2.CreateEventReq( 

689 title="Dummy Title", 

690 content="Dummy content.", 

691 offline_information=events_pb2.OfflineEventInformation( 

692 address="Near Null Island", 

693 lat=0.1, 

694 lng=0.2, 

695 ), 

696 start_time=Timestamp_from_datetime(start_time), 

697 end_time=Timestamp_from_datetime(end_time), 

698 timezone="UTC", 

699 ) 

700 ) 

701 

702 event_id = res.event_id 

703 

704 with events_session(token4) as api: 

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

706 

707 with events_session(token5) as api: 

708 api.SetEventAttendance( 

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

710 ) 

711 

712 with events_session(token6) as api: 

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

714 api.SetEventAttendance( 

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

716 ) 

717 

718 time_before_update = now() 

719 

720 with events_session(token1) as api: 

721 res = api.UpdateEvent( 

722 events_pb2.UpdateEventReq( 

723 event_id=event_id, 

724 ) 

725 ) 

726 

727 with events_session(token1) as api: 

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

729 

730 assert res.is_next 

731 assert res.title == "Dummy Title" 

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

733 assert res.content == "Dummy content." 

734 assert not res.photo_url 

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

736 assert res.offline_information.lat == 0.1 

737 assert res.offline_information.lng == 0.2 

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

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

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

741 assert res.creator_user_id == user1.id 

742 assert to_aware_datetime(res.start_time) == start_time 

743 assert to_aware_datetime(res.end_time) == end_time 

744 # assert res.timezone == "UTC" 

745 assert res.start_time_display 

746 assert res.end_time_display 

747 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

748 assert res.organizer 

749 assert res.subscriber 

750 assert res.going_count == 2 

751 assert res.maybe_count == 1 

752 assert res.organizer_count == 1 

753 assert res.subscriber_count == 3 

754 assert res.owner_user_id == user1.id 

755 assert not res.owner_community_id 

756 assert not res.owner_group_id 

757 assert res.thread.thread_id 

758 assert res.can_edit 

759 assert not res.can_moderate 

760 

761 with events_session(token2) as api: 

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

763 

764 assert res.is_next 

765 assert res.title == "Dummy Title" 

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

767 assert res.content == "Dummy content." 

768 assert not res.photo_url 

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

770 assert res.offline_information.lat == 0.1 

771 assert res.offline_information.lng == 0.2 

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

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

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

775 assert res.creator_user_id == user1.id 

776 assert to_aware_datetime(res.start_time) == start_time 

777 assert to_aware_datetime(res.end_time) == end_time 

778 # assert res.timezone == "UTC" 

779 assert res.start_time_display 

780 assert res.end_time_display 

781 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

782 assert not res.organizer 

783 assert not res.subscriber 

784 assert res.going_count == 2 

785 assert res.maybe_count == 1 

786 assert res.organizer_count == 1 

787 assert res.subscriber_count == 3 

788 assert res.owner_user_id == user1.id 

789 assert not res.owner_community_id 

790 assert not res.owner_group_id 

791 assert res.thread.thread_id 

792 assert res.can_edit 

793 assert res.can_moderate 

794 

795 with events_session(token3) as api: 

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

797 

798 assert res.is_next 

799 assert res.title == "Dummy Title" 

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

801 assert res.content == "Dummy content." 

802 assert not res.photo_url 

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

804 assert res.offline_information.lat == 0.1 

805 assert res.offline_information.lng == 0.2 

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

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

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

809 assert res.creator_user_id == user1.id 

810 assert to_aware_datetime(res.start_time) == start_time 

811 assert to_aware_datetime(res.end_time) == end_time 

812 # assert res.timezone == "UTC" 

813 assert res.start_time_display 

814 assert res.end_time_display 

815 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

816 assert not res.organizer 

817 assert not res.subscriber 

818 assert res.going_count == 2 

819 assert res.maybe_count == 1 

820 assert res.organizer_count == 1 

821 assert res.subscriber_count == 3 

822 assert res.owner_user_id == user1.id 

823 assert not res.owner_community_id 

824 assert not res.owner_group_id 

825 assert res.thread.thread_id 

826 assert not res.can_edit 

827 assert not res.can_moderate 

828 

829 with events_session(token1) as api: 

830 res = api.UpdateEvent( 

831 events_pb2.UpdateEventReq( 

832 event_id=event_id, 

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

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

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

836 start_time=Timestamp_from_datetime(start_time), 

837 end_time=Timestamp_from_datetime(end_time), 

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

839 ) 

840 ) 

841 

842 assert res.is_next 

843 assert res.title == "Dummy Title" 

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

845 assert res.content == "Dummy content." 

846 assert not res.photo_url 

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

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

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

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

851 assert res.creator_user_id == user1.id 

852 assert to_aware_datetime(res.start_time) == start_time 

853 assert to_aware_datetime(res.end_time) == end_time 

854 # assert res.timezone == "UTC" 

855 assert res.start_time_display 

856 assert res.end_time_display 

857 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

858 assert res.organizer 

859 assert res.subscriber 

860 assert res.going_count == 2 

861 assert res.maybe_count == 1 

862 assert res.organizer_count == 1 

863 assert res.subscriber_count == 3 

864 assert res.owner_user_id == user1.id 

865 assert not res.owner_community_id 

866 assert not res.owner_group_id 

867 assert res.thread.thread_id 

868 assert res.can_edit 

869 assert not res.can_moderate 

870 

871 event_id = res.event_id 

872 

873 with events_session(token2) as api: 

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

875 

876 assert res.is_next 

877 assert res.title == "Dummy Title" 

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

879 assert res.content == "Dummy content." 

880 assert not res.photo_url 

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

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

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

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

885 assert res.creator_user_id == user1.id 

886 assert to_aware_datetime(res.start_time) == start_time 

887 assert to_aware_datetime(res.end_time) == end_time 

888 # assert res.timezone == "UTC" 

889 assert res.start_time_display 

890 assert res.end_time_display 

891 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

892 assert not res.organizer 

893 assert not res.subscriber 

894 assert res.going_count == 2 

895 assert res.maybe_count == 1 

896 assert res.organizer_count == 1 

897 assert res.subscriber_count == 3 

898 assert res.owner_user_id == user1.id 

899 assert not res.owner_community_id 

900 assert not res.owner_group_id 

901 assert res.thread.thread_id 

902 assert res.can_edit 

903 assert res.can_moderate 

904 

905 with events_session(token3) as api: 

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

907 

908 assert res.is_next 

909 assert res.title == "Dummy Title" 

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

911 assert res.content == "Dummy content." 

912 assert not res.photo_url 

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

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

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

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

917 assert res.creator_user_id == user1.id 

918 assert to_aware_datetime(res.start_time) == start_time 

919 assert to_aware_datetime(res.end_time) == end_time 

920 # assert res.timezone == "UTC" 

921 assert res.start_time_display 

922 assert res.end_time_display 

923 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

924 assert not res.organizer 

925 assert not res.subscriber 

926 assert res.going_count == 2 

927 assert res.maybe_count == 1 

928 assert res.organizer_count == 1 

929 assert res.subscriber_count == 3 

930 assert res.owner_user_id == user1.id 

931 assert not res.owner_community_id 

932 assert not res.owner_group_id 

933 assert res.thread.thread_id 

934 assert not res.can_edit 

935 assert not res.can_moderate 

936 

937 with events_session(token1) as api: 

938 res = api.UpdateEvent( 

939 events_pb2.UpdateEventReq( 

940 event_id=event_id, 

941 offline_information=events_pb2.OfflineEventInformation( 

942 address="Near Null Island", 

943 lat=0.1, 

944 lng=0.2, 

945 ), 

946 ) 

947 ) 

948 

949 with events_session(token3) as api: 

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

951 

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

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

954 assert res.offline_information.lat == 0.1 

955 assert res.offline_information.lng == 0.2 

956 

957 

958def test_UpdateEvent_all(db): 

959 # event creator 

960 user1, token1 = generate_user() 

961 # community moderator 

962 user2, token2 = generate_user() 

963 # third parties 

964 user3, token3 = generate_user() 

965 user4, token4 = generate_user() 

966 user5, token5 = generate_user() 

967 user6, token6 = generate_user() 

968 

969 with session_scope() as session: 

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

971 

972 time_before = now() 

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

974 end_time = start_time + timedelta(hours=1.5) 

975 

976 event_ids = [] 

977 

978 with events_session(token1) as api: 

979 res = api.CreateEvent( 

980 events_pb2.CreateEventReq( 

981 title="Dummy Title", 

982 content="0th occurrence", 

983 offline_information=events_pb2.OfflineEventInformation( 

984 address="Near Null Island", 

985 lat=0.1, 

986 lng=0.2, 

987 ), 

988 start_time=Timestamp_from_datetime(start_time), 

989 end_time=Timestamp_from_datetime(end_time), 

990 timezone="UTC", 

991 ) 

992 ) 

993 

994 event_id = res.event_id 

995 event_ids.append(event_id) 

996 

997 with events_session(token4) as api: 

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

999 

1000 with events_session(token5) as api: 

1001 api.SetEventAttendance( 

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

1003 ) 

1004 

1005 with events_session(token6) as api: 

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

1007 api.SetEventAttendance( 

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

1009 ) 

1010 

1011 with events_session(token1) as api: 

1012 for i in range(5): 

1013 res = api.ScheduleEvent( 

1014 events_pb2.ScheduleEventReq( 

1015 event_id=event_ids[-1], 

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

1017 online_information=events_pb2.OnlineEventInformation( 

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

1019 ), 

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

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

1022 timezone="UTC", 

1023 ) 

1024 ) 

1025 

1026 event_ids.append(res.event_id) 

1027 

1028 updated_event_id = event_ids[3] 

1029 

1030 time_before_update = now() 

1031 

1032 with events_session(token1) as api: 

1033 res = api.UpdateEvent( 

1034 events_pb2.UpdateEventReq( 

1035 event_id=updated_event_id, 

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

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

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

1039 update_all_future=True, 

1040 ) 

1041 ) 

1042 

1043 time_after_update = now() 

1044 

1045 with events_session(token2) as api: 

1046 for i in range(3): 

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

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

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

1050 

1051 for i in range(3, 6): 

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

1053 assert res.content == "New content." 

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

1055 

1056 

1057def test_GetEvent(db): 

1058 # event creator 

1059 user1, token1 = generate_user() 

1060 # community moderator 

1061 user2, token2 = generate_user() 

1062 # third parties 

1063 user3, token3 = generate_user() 

1064 user4, token4 = generate_user() 

1065 user5, token5 = generate_user() 

1066 user6, token6 = generate_user() 

1067 

1068 with session_scope() as session: 

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

1070 

1071 time_before = now() 

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

1073 end_time = start_time + timedelta(hours=3) 

1074 

1075 with events_session(token1) as api: 

1076 # in person event 

1077 res = api.CreateEvent( 

1078 events_pb2.CreateEventReq( 

1079 title="Dummy Title", 

1080 content="Dummy content.", 

1081 offline_information=events_pb2.OfflineEventInformation( 

1082 address="Near Null Island", 

1083 lat=0.1, 

1084 lng=0.2, 

1085 ), 

1086 start_time=Timestamp_from_datetime(start_time), 

1087 end_time=Timestamp_from_datetime(end_time), 

1088 timezone="UTC", 

1089 ) 

1090 ) 

1091 

1092 event_id = res.event_id 

1093 

1094 with events_session(token4) as api: 

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

1096 

1097 with events_session(token5) as api: 

1098 api.SetEventAttendance( 

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

1100 ) 

1101 

1102 with events_session(token6) as api: 

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

1104 api.SetEventAttendance( 

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

1106 ) 

1107 

1108 with events_session(token1) as api: 

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

1110 

1111 assert res.is_next 

1112 assert res.title == "Dummy Title" 

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

1114 assert res.content == "Dummy content." 

1115 assert not res.photo_url 

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

1117 assert res.offline_information.lat == 0.1 

1118 assert res.offline_information.lng == 0.2 

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

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

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

1122 assert res.creator_user_id == user1.id 

1123 assert to_aware_datetime(res.start_time) == start_time 

1124 assert to_aware_datetime(res.end_time) == end_time 

1125 # assert res.timezone == "UTC" 

1126 assert res.start_time_display 

1127 assert res.end_time_display 

1128 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_GOING 

1129 assert res.organizer 

1130 assert res.subscriber 

1131 assert res.going_count == 2 

1132 assert res.maybe_count == 1 

1133 assert res.organizer_count == 1 

1134 assert res.subscriber_count == 3 

1135 assert res.owner_user_id == user1.id 

1136 assert not res.owner_community_id 

1137 assert not res.owner_group_id 

1138 assert res.thread.thread_id 

1139 assert res.can_edit 

1140 assert not res.can_moderate 

1141 

1142 with events_session(token2) as api: 

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

1144 

1145 assert res.is_next 

1146 assert res.title == "Dummy Title" 

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

1148 assert res.content == "Dummy content." 

1149 assert not res.photo_url 

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

1151 assert res.offline_information.lat == 0.1 

1152 assert res.offline_information.lng == 0.2 

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

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

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

1156 assert res.creator_user_id == user1.id 

1157 assert to_aware_datetime(res.start_time) == start_time 

1158 assert to_aware_datetime(res.end_time) == end_time 

1159 # assert res.timezone == "UTC" 

1160 assert res.start_time_display 

1161 assert res.end_time_display 

1162 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1163 assert not res.organizer 

1164 assert not res.subscriber 

1165 assert res.going_count == 2 

1166 assert res.maybe_count == 1 

1167 assert res.organizer_count == 1 

1168 assert res.subscriber_count == 3 

1169 assert res.owner_user_id == user1.id 

1170 assert not res.owner_community_id 

1171 assert not res.owner_group_id 

1172 assert res.thread.thread_id 

1173 assert res.can_edit 

1174 assert res.can_moderate 

1175 

1176 with events_session(token3) as api: 

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

1178 

1179 assert res.is_next 

1180 assert res.title == "Dummy Title" 

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

1182 assert res.content == "Dummy content." 

1183 assert not res.photo_url 

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

1185 assert res.offline_information.lat == 0.1 

1186 assert res.offline_information.lng == 0.2 

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

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

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

1190 assert res.creator_user_id == user1.id 

1191 assert to_aware_datetime(res.start_time) == start_time 

1192 assert to_aware_datetime(res.end_time) == end_time 

1193 # assert res.timezone == "UTC" 

1194 assert res.start_time_display 

1195 assert res.end_time_display 

1196 assert res.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1197 assert not res.organizer 

1198 assert not res.subscriber 

1199 assert res.going_count == 2 

1200 assert res.maybe_count == 1 

1201 assert res.organizer_count == 1 

1202 assert res.subscriber_count == 3 

1203 assert res.owner_user_id == user1.id 

1204 assert not res.owner_community_id 

1205 assert not res.owner_group_id 

1206 assert res.thread.thread_id 

1207 assert not res.can_edit 

1208 assert not res.can_moderate 

1209 

1210 

1211def test_CancelEvent(db): 

1212 # event creator 

1213 user1, token1 = generate_user() 

1214 # community moderator 

1215 user2, token2 = generate_user() 

1216 # third parties 

1217 user3, token3 = generate_user() 

1218 user4, token4 = generate_user() 

1219 user5, token5 = generate_user() 

1220 user6, token6 = generate_user() 

1221 

1222 with session_scope() as session: 

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

1224 

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

1226 end_time = start_time + timedelta(hours=3) 

1227 

1228 with events_session(token1) as api: 

1229 res = api.CreateEvent( 

1230 events_pb2.CreateEventReq( 

1231 title="Dummy Title", 

1232 content="Dummy content.", 

1233 offline_information=events_pb2.OfflineEventInformation( 

1234 address="Near Null Island", 

1235 lat=0.1, 

1236 lng=0.2, 

1237 ), 

1238 start_time=Timestamp_from_datetime(start_time), 

1239 end_time=Timestamp_from_datetime(end_time), 

1240 timezone="UTC", 

1241 ) 

1242 ) 

1243 

1244 event_id = res.event_id 

1245 

1246 with events_session(token4) as api: 

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

1248 

1249 with events_session(token5) as api: 

1250 api.SetEventAttendance( 

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

1252 ) 

1253 

1254 with events_session(token6) as api: 

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

1256 api.SetEventAttendance( 

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

1258 ) 

1259 

1260 with events_session(token1) as api: 

1261 res = api.CancelEvent( 

1262 events_pb2.CancelEventReq( 

1263 event_id=event_id, 

1264 ) 

1265 ) 

1266 

1267 with events_session(token1) as api: 

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

1269 assert res.is_cancelled 

1270 

1271 with events_session(token1) as api: 

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

1273 api.UpdateEvent( 

1274 events_pb2.UpdateEventReq( 

1275 event_id=event_id, 

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

1277 ) 

1278 ) 

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

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

1281 

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

1283 api.InviteEventOrganizer( 

1284 events_pb2.InviteEventOrganizerReq( 

1285 event_id=event_id, 

1286 user_id=user3.id, 

1287 ) 

1288 ) 

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

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

1291 

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

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

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

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

1296 

1297 with events_session(token3) as api: 

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

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

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

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

1302 

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

1304 api.SetEventAttendance( 

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

1306 ) 

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

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

1309 

1310 with events_session(token1) as api: 

1311 for include_cancelled in [True, False]: 

1312 res = api.ListEventOccurrences( 

1313 events_pb2.ListEventOccurrencesReq( 

1314 event_id=event_id, 

1315 include_cancelled=include_cancelled, 

1316 ) 

1317 ) 

1318 if include_cancelled: 

1319 assert len(res.events) > 0 

1320 else: 

1321 assert len(res.events) == 0 

1322 

1323 res = api.ListMyEvents( 

1324 events_pb2.ListMyEventsReq( 

1325 include_cancelled=include_cancelled, 

1326 ) 

1327 ) 

1328 if include_cancelled: 

1329 assert len(res.events) > 0 

1330 else: 

1331 assert len(res.events) == 0 

1332 

1333 

1334def test_ListEventAttendees(db): 

1335 # event creator 

1336 user1, token1 = generate_user() 

1337 # others 

1338 user2, token2 = generate_user() 

1339 user3, token3 = generate_user() 

1340 user4, token4 = generate_user() 

1341 user5, token5 = generate_user() 

1342 user6, token6 = generate_user() 

1343 

1344 with session_scope() as session: 

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

1346 

1347 with events_session(token1) as api: 

1348 event_id = api.CreateEvent( 

1349 events_pb2.CreateEventReq( 

1350 title="Dummy Title", 

1351 content="Dummy content.", 

1352 offline_information=events_pb2.OfflineEventInformation( 

1353 address="Near Null Island", 

1354 lat=0.1, 

1355 lng=0.2, 

1356 ), 

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

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

1359 timezone="UTC", 

1360 ) 

1361 ).event_id 

1362 

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

1364 with events_session(token) as api: 

1365 api.SetEventAttendance( 

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

1367 ) 

1368 

1369 with events_session(token6) as api: 

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

1371 

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

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

1374 

1375 res = api.ListEventAttendees( 

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

1377 ) 

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

1379 

1380 res = api.ListEventAttendees( 

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

1382 ) 

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

1384 assert not res.next_page_token 

1385 

1386 

1387def test_ListEventSubscribers(db): 

1388 # event creator 

1389 user1, token1 = generate_user() 

1390 # others 

1391 user2, token2 = generate_user() 

1392 user3, token3 = generate_user() 

1393 user4, token4 = generate_user() 

1394 user5, token5 = generate_user() 

1395 user6, token6 = generate_user() 

1396 

1397 with session_scope() as session: 

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

1399 

1400 with events_session(token1) as api: 

1401 event_id = api.CreateEvent( 

1402 events_pb2.CreateEventReq( 

1403 title="Dummy Title", 

1404 content="Dummy content.", 

1405 offline_information=events_pb2.OfflineEventInformation( 

1406 address="Near Null Island", 

1407 lat=0.1, 

1408 lng=0.2, 

1409 ), 

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

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

1412 timezone="UTC", 

1413 ) 

1414 ).event_id 

1415 

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

1417 with events_session(token) as api: 

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

1419 

1420 with events_session(token6) as api: 

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

1422 

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

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

1425 

1426 res = api.ListEventSubscribers( 

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

1428 ) 

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

1430 

1431 res = api.ListEventSubscribers( 

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

1433 ) 

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

1435 assert not res.next_page_token 

1436 

1437 

1438def test_ListEventOrganizers(db): 

1439 # event creator 

1440 user1, token1 = generate_user() 

1441 # others 

1442 user2, token2 = generate_user() 

1443 user3, token3 = generate_user() 

1444 user4, token4 = generate_user() 

1445 user5, token5 = generate_user() 

1446 user6, token6 = generate_user() 

1447 

1448 with session_scope() as session: 

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

1450 

1451 with events_session(token1) as api: 

1452 event_id = api.CreateEvent( 

1453 events_pb2.CreateEventReq( 

1454 title="Dummy Title", 

1455 content="Dummy content.", 

1456 offline_information=events_pb2.OfflineEventInformation( 

1457 address="Near Null Island", 

1458 lat=0.1, 

1459 lng=0.2, 

1460 ), 

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

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

1463 timezone="UTC", 

1464 ) 

1465 ).event_id 

1466 

1467 with events_session(token1) as api: 

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

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

1470 

1471 with events_session(token6) as api: 

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

1473 

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

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

1476 

1477 res = api.ListEventOrganizers( 

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

1479 ) 

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

1481 

1482 res = api.ListEventOrganizers( 

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

1484 ) 

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

1486 assert not res.next_page_token 

1487 

1488 

1489def test_TransferEvent(db): 

1490 user1, token1 = generate_user() 

1491 user2, token2 = generate_user() 

1492 user3, token3 = generate_user() 

1493 user4, token4 = generate_user() 

1494 

1495 with session_scope() as session: 

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

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

1498 c_id = c.id 

1499 h_id = h.id 

1500 

1501 with events_session(token1) as api: 

1502 event_id = api.CreateEvent( 

1503 events_pb2.CreateEventReq( 

1504 title="Dummy Title", 

1505 content="Dummy content.", 

1506 offline_information=events_pb2.OfflineEventInformation( 

1507 address="Near Null Island", 

1508 lat=0.1, 

1509 lng=0.2, 

1510 ), 

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

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

1513 timezone="UTC", 

1514 ) 

1515 ).event_id 

1516 

1517 api.TransferEvent( 

1518 events_pb2.TransferEventReq( 

1519 event_id=event_id, 

1520 new_owner_community_id=c_id, 

1521 ) 

1522 ) 

1523 

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

1525 api.TransferEvent( 

1526 events_pb2.TransferEventReq( 

1527 event_id=event_id, 

1528 new_owner_group_id=h_id, 

1529 ) 

1530 ) 

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

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

1533 

1534 event_id = api.CreateEvent( 

1535 events_pb2.CreateEventReq( 

1536 title="Dummy Title", 

1537 content="Dummy content.", 

1538 offline_information=events_pb2.OfflineEventInformation( 

1539 address="Near Null Island", 

1540 lat=0.1, 

1541 lng=0.2, 

1542 ), 

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

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

1545 timezone="UTC", 

1546 ) 

1547 ).event_id 

1548 

1549 api.TransferEvent( 

1550 events_pb2.TransferEventReq( 

1551 event_id=event_id, 

1552 new_owner_group_id=h_id, 

1553 ) 

1554 ) 

1555 

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

1557 api.TransferEvent( 

1558 events_pb2.TransferEventReq( 

1559 event_id=event_id, 

1560 new_owner_community_id=c_id, 

1561 ) 

1562 ) 

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

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

1565 

1566 

1567def test_SetEventSubscription(db): 

1568 user1, token1 = generate_user() 

1569 user2, token2 = generate_user() 

1570 

1571 with session_scope() as session: 

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

1573 

1574 with events_session(token1) as api: 

1575 event_id = api.CreateEvent( 

1576 events_pb2.CreateEventReq( 

1577 title="Dummy Title", 

1578 content="Dummy content.", 

1579 offline_information=events_pb2.OfflineEventInformation( 

1580 address="Near Null Island", 

1581 lat=0.1, 

1582 lng=0.2, 

1583 ), 

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

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

1586 timezone="UTC", 

1587 ) 

1588 ).event_id 

1589 

1590 with events_session(token2) as api: 

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

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

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

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

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

1596 

1597 

1598def test_SetEventAttendance(db): 

1599 user1, token1 = generate_user() 

1600 user2, token2 = generate_user() 

1601 

1602 with session_scope() as session: 

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

1604 

1605 with events_session(token1) as api: 

1606 event_id = api.CreateEvent( 

1607 events_pb2.CreateEventReq( 

1608 title="Dummy Title", 

1609 content="Dummy content.", 

1610 offline_information=events_pb2.OfflineEventInformation( 

1611 address="Near Null Island", 

1612 lat=0.1, 

1613 lng=0.2, 

1614 ), 

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

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

1617 timezone="UTC", 

1618 ) 

1619 ).event_id 

1620 

1621 with events_session(token2) as api: 

1622 assert ( 

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

1624 == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1625 ) 

1626 api.SetEventAttendance( 

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

1628 ) 

1629 assert ( 

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

1631 == events_pb2.ATTENDANCE_STATE_GOING 

1632 ) 

1633 api.SetEventAttendance( 

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

1635 ) 

1636 assert ( 

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

1638 == events_pb2.ATTENDANCE_STATE_MAYBE 

1639 ) 

1640 api.SetEventAttendance( 

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

1642 ) 

1643 assert ( 

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

1645 == events_pb2.ATTENDANCE_STATE_NOT_GOING 

1646 ) 

1647 

1648 

1649def test_InviteEventOrganizer(db): 

1650 user1, token1 = generate_user() 

1651 user2, token2 = generate_user() 

1652 

1653 with session_scope() as session: 

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

1655 

1656 with events_session(token1) as api: 

1657 event_id = api.CreateEvent( 

1658 events_pb2.CreateEventReq( 

1659 title="Dummy Title", 

1660 content="Dummy content.", 

1661 offline_information=events_pb2.OfflineEventInformation( 

1662 address="Near Null Island", 

1663 lat=0.1, 

1664 lng=0.2, 

1665 ), 

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

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

1668 timezone="UTC", 

1669 ) 

1670 ).event_id 

1671 

1672 with events_session(token2) as api: 

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

1674 

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

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

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

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

1679 

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

1681 

1682 with events_session(token1) as api: 

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

1684 

1685 with events_session(token2) as api: 

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

1687 

1688 

1689def test_ListEventOccurrences(db): 

1690 user1, token1 = generate_user() 

1691 user2, token2 = generate_user() 

1692 user3, token3 = generate_user() 

1693 

1694 with session_scope() as session: 

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

1696 

1697 start = now() 

1698 

1699 event_ids = [] 

1700 

1701 with events_session(token1) as api: 

1702 res = api.CreateEvent( 

1703 events_pb2.CreateEventReq( 

1704 title="First occurrence", 

1705 content="Dummy content.", 

1706 parent_community_id=c_id, 

1707 online_information=events_pb2.OnlineEventInformation( 

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

1709 ), 

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

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

1712 timezone="UTC", 

1713 ) 

1714 ) 

1715 

1716 event_ids.append(res.event_id) 

1717 

1718 for i in range(5): 

1719 res = api.ScheduleEvent( 

1720 events_pb2.ScheduleEventReq( 

1721 event_id=event_ids[-1], 

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

1723 online_information=events_pb2.OnlineEventInformation( 

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

1725 ), 

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

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

1728 timezone="UTC", 

1729 ) 

1730 ) 

1731 

1732 event_ids.append(res.event_id) 

1733 

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

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

1736 

1737 res = api.ListEventOccurrences( 

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

1739 ) 

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

1741 

1742 res = api.ListEventOccurrences( 

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

1744 ) 

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

1746 assert not res.next_page_token 

1747 

1748 

1749def test_ListMyEvents(db): 

1750 user1, token1 = generate_user() 

1751 user2, token2 = generate_user() 

1752 user3, token3 = generate_user() 

1753 user4, token4 = generate_user() 

1754 user5, token5 = generate_user() 

1755 

1756 with session_scope() as session: 

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

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

1759 

1760 start = now() 

1761 

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

1763 if online: 

1764 return events_pb2.CreateEventReq( 

1765 title="Dummy Online Title", 

1766 content="Dummy content.", 

1767 online_information=events_pb2.OnlineEventInformation( 

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

1769 ), 

1770 parent_community_id=community_id, 

1771 timezone="UTC", 

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

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

1774 ) 

1775 else: 

1776 return events_pb2.CreateEventReq( 

1777 title="Dummy Offline Title", 

1778 content="Dummy content.", 

1779 offline_information=events_pb2.OfflineEventInformation( 

1780 address="Near Null Island", 

1781 lat=0.1, 

1782 lng=0.2, 

1783 ), 

1784 timezone="UTC", 

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

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

1787 ) 

1788 

1789 with events_session(token1) as api: 

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

1791 

1792 with events_session(token2) as api: 

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

1794 

1795 with events_session(token1) as api: 

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

1797 

1798 with events_session(token2) as api: 

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

1800 

1801 with events_session(token3) as api: 

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

1803 

1804 with events_session(token4) as api: 

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

1806 

1807 with events_session(token1) as api: 

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

1809 

1810 with events_session(token1) as api: 

1811 api.SetEventAttendance( 

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

1813 ) 

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

1815 

1816 with events_session(token2) as api: 

1817 api.SetEventAttendance( 

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

1819 ) 

1820 

1821 with events_session(token3) as api: 

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

1823 

1824 with events_session(token1) as api: 

1825 # test pagination first 

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

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

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

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

1830 assert not res.next_page_token 

1831 

1832 res = api.ListMyEvents( 

1833 events_pb2.ListMyEventsReq( 

1834 subscribed=True, 

1835 attending=True, 

1836 organizing=True, 

1837 ) 

1838 ) 

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

1840 

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

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

1843 

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

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

1846 

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

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

1849 

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

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

1852 

1853 with events_session(token2) as api: 

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

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

1856 

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

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

1859 

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

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

1862 

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

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

1865 

1866 with events_session(token3) as api: 

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

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

1869 

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

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

1872 

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

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

1875 

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

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

1878 

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

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

1881 

1882 with events_session(token5) as api: 

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

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

1885 

1886 

1887def test_RemoveEventOrganizer(db): 

1888 user1, token1 = generate_user() 

1889 user2, token2 = generate_user() 

1890 

1891 with session_scope() as session: 

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

1893 

1894 with events_session(token1) as api: 

1895 event_id = api.CreateEvent( 

1896 events_pb2.CreateEventReq( 

1897 title="Dummy Title", 

1898 content="Dummy content.", 

1899 offline_information=events_pb2.OfflineEventInformation( 

1900 address="Near Null Island", 

1901 lat=0.1, 

1902 lng=0.2, 

1903 ), 

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

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

1906 timezone="UTC", 

1907 ) 

1908 ).event_id 

1909 

1910 with events_session(token2) as api: 

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

1912 

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

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

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

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

1917 

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

1919 

1920 with events_session(token1) as api: 

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

1922 

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

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

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

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

1927 

1928 with events_session(token2) as api: 

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

1930 assert res.organizer 

1931 assert res.organizer_count == 2 

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

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

1934 

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

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

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

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

1939 

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

1941 assert not res.organizer 

1942 assert res.organizer_count == 1 

1943 

1944 

1945def test_ListEventAttendees_regression(db): 

1946 # see issue #1617: 

1947 # 

1948 # 1. Create an event 

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

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

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

1952 # 

1953 # **Expected behaviour** 

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

1955 # 

1956 # **Actual/current behaviour** 

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

1958 

1959 user1, token1 = generate_user() 

1960 user2, token2 = generate_user() 

1961 user3, token3 = generate_user() 

1962 user4, token4 = generate_user() 

1963 user5, token5 = generate_user() 

1964 

1965 with session_scope() as session: 

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

1967 

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

1969 end_time = start_time + timedelta(hours=3) 

1970 

1971 with events_session(token1) as api: 

1972 res = api.CreateEvent( 

1973 events_pb2.CreateEventReq( 

1974 title="Dummy Title", 

1975 content="Dummy content.", 

1976 online_information=events_pb2.OnlineEventInformation( 

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

1978 ), 

1979 parent_community_id=c_id, 

1980 start_time=Timestamp_from_datetime(start_time), 

1981 end_time=Timestamp_from_datetime(end_time), 

1982 timezone="UTC", 

1983 ) 

1984 ) 

1985 

1986 res = api.TransferEvent( 

1987 events_pb2.TransferEventReq( 

1988 event_id=res.event_id, 

1989 new_owner_community_id=c_id, 

1990 ) 

1991 ) 

1992 

1993 event_id = res.event_id 

1994 

1995 api.SetEventAttendance( 

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

1997 ) 

1998 api.SetEventAttendance( 

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

2000 ) 

2001 

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

2003 assert len(res.attendee_user_ids) == 1 

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

2005 

2006 

2007def test_event_threads(db): 

2008 user1, token1 = generate_user() 

2009 user2, token2 = generate_user() 

2010 user3, token3 = generate_user() 

2011 user4, token4 = generate_user() 

2012 

2013 with session_scope() as session: 

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

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

2016 c_id = c.id 

2017 h_id = h.id 

2018 

2019 with events_session(token1) as api: 

2020 event = api.CreateEvent( 

2021 events_pb2.CreateEventReq( 

2022 title="Dummy Title", 

2023 content="Dummy content.", 

2024 offline_information=events_pb2.OfflineEventInformation( 

2025 address="Near Null Island", 

2026 lat=0.1, 

2027 lng=0.2, 

2028 ), 

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

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

2031 timezone="UTC", 

2032 ) 

2033 ) 

2034 

2035 with threads_session(token2) as api: 

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

2037 

2038 with events_session(token3) as api: 

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

2040 assert res.thread.num_responses == 1 

2041 

2042 with threads_session(token3) as api: 

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

2044 assert len(ret.replies) == 1 

2045 assert not ret.next_page_token 

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

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

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

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

2050 

2051 

2052def test_can_overlap_other_events_schedule_regression(db): 

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

2054 user, token = generate_user() 

2055 

2056 with session_scope() as session: 

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

2058 

2059 start = now() 

2060 

2061 with events_session(token) as api: 

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

2063 api.CreateEvent( 

2064 events_pb2.CreateEventReq( 

2065 title="Dummy Title", 

2066 content="Dummy content.", 

2067 parent_community_id=c_id, 

2068 online_information=events_pb2.OnlineEventInformation( 

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

2070 ), 

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

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

2073 timezone="UTC", 

2074 ) 

2075 ) 

2076 

2077 # this event 

2078 res = api.CreateEvent( 

2079 events_pb2.CreateEventReq( 

2080 title="Dummy Title", 

2081 content="Dummy content.", 

2082 parent_community_id=c_id, 

2083 online_information=events_pb2.OnlineEventInformation( 

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

2085 ), 

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

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

2088 timezone="UTC", 

2089 ) 

2090 ) 

2091 

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

2093 api.ScheduleEvent( 

2094 events_pb2.ScheduleEventReq( 

2095 event_id=res.event_id, 

2096 content="New event occurrence", 

2097 offline_information=events_pb2.OfflineEventInformation( 

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

2099 lat=0.3, 

2100 lng=0.2, 

2101 ), 

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

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

2104 timezone="UTC", 

2105 ) 

2106 ) 

2107 

2108 

2109def test_can_overlap_other_events_update_regression(db): 

2110 user, token = generate_user() 

2111 

2112 with session_scope() as session: 

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

2114 

2115 start = now() 

2116 

2117 with events_session(token) as api: 

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

2119 api.CreateEvent( 

2120 events_pb2.CreateEventReq( 

2121 title="Dummy Title", 

2122 content="Dummy content.", 

2123 parent_community_id=c_id, 

2124 online_information=events_pb2.OnlineEventInformation( 

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

2126 ), 

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

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

2129 timezone="UTC", 

2130 ) 

2131 ) 

2132 

2133 res = api.CreateEvent( 

2134 events_pb2.CreateEventReq( 

2135 title="Dummy Title", 

2136 content="Dummy content.", 

2137 parent_community_id=c_id, 

2138 online_information=events_pb2.OnlineEventInformation( 

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

2140 ), 

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

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

2143 timezone="UTC", 

2144 ) 

2145 ) 

2146 

2147 event_id = api.ScheduleEvent( 

2148 events_pb2.ScheduleEventReq( 

2149 event_id=res.event_id, 

2150 content="New event occurrence", 

2151 offline_information=events_pb2.OfflineEventInformation( 

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

2153 lat=0.3, 

2154 lng=0.2, 

2155 ), 

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

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

2158 timezone="UTC", 

2159 ) 

2160 ).event_id 

2161 

2162 # can overlap with this current existing occurrence 

2163 api.UpdateEvent( 

2164 events_pb2.UpdateEventReq( 

2165 event_id=event_id, 

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

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

2168 ) 

2169 ) 

2170 

2171 api.UpdateEvent( 

2172 events_pb2.UpdateEventReq( 

2173 event_id=event_id, 

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

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

2176 ) 

2177 ) 

2178 

2179 

2180def test_list_past_events_regression(db): 

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

2182 user, token = generate_user() 

2183 

2184 with session_scope() as session: 

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

2186 

2187 start = now() 

2188 

2189 with events_session(token) as api: 

2190 api.CreateEvent( 

2191 events_pb2.CreateEventReq( 

2192 title="Dummy Title", 

2193 content="Dummy content.", 

2194 parent_community_id=c_id, 

2195 online_information=events_pb2.OnlineEventInformation( 

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

2197 ), 

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

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

2200 timezone="UTC", 

2201 ) 

2202 ) 

2203 

2204 with session_scope() as session: 

2205 session.execute( 

2206 update(EventOccurrence).values( 

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

2208 ) 

2209 ) 

2210 

2211 with events_session(token) as api: 

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

2213 assert len(res.events) == 1 

2214 

2215 

2216def test_community_invite_requests(db): 

2217 user1, token1 = generate_user(complete_profile=True) 

2218 user2, token2 = generate_user() 

2219 user3, token3 = generate_user() 

2220 user4, token4 = generate_user() 

2221 user5, token5 = generate_user(is_superuser=True) 

2222 

2223 with session_scope() as session: 

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

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

2226 

2227 enforce_community_memberships() 

2228 

2229 with events_session(token1) as api: 

2230 res = api.CreateEvent( 

2231 events_pb2.CreateEventReq( 

2232 title="Dummy Title", 

2233 content="Dummy content.", 

2234 parent_community_id=c_id, 

2235 online_information=events_pb2.OnlineEventInformation( 

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

2237 ), 

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

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

2240 timezone="UTC", 

2241 ) 

2242 ) 

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

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

2245 

2246 event_id = res.event_id 

2247 

2248 with mock_notification_email() as mock: 

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

2250 assert mock.call_count == 1 

2251 e = email_fields(mock) 

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

2253 

2254 assert user_url in e.plain 

2255 assert event_url in e.plain 

2256 

2257 # can't send another req 

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

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

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

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

2262 

2263 # another user can send one though 

2264 with events_session(token3) as api: 

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

2266 

2267 # but not a non-admin 

2268 with events_session(token2) as api: 

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

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

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

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

2273 

2274 with real_admin_session(token5) as admin: 

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

2276 assert len(res.requests) == 2 

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

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

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

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

2281 

2282 admin.DecideEventCommunityInviteRequest( 

2283 admin_pb2.DecideEventCommunityInviteRequestReq( 

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

2285 approve=False, 

2286 ) 

2287 ) 

2288 

2289 admin.DecideEventCommunityInviteRequest( 

2290 admin_pb2.DecideEventCommunityInviteRequestReq( 

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

2292 approve=True, 

2293 ) 

2294 ) 

2295 

2296 # not after approve 

2297 with events_session(token4) as api: 

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

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

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

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