Skip to content

Commit 9a12fa3

Browse files
committed
more service tests
Signed-off-by: dartcafe <github@dartcafe.de>
1 parent 93e1243 commit 9a12fa3

File tree

2 files changed

+286
-0
lines changed

2 files changed

+286
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Polls\Tests\Unit\Service;
10+
11+
use OCA\Polls\Db\Comment;
12+
use OCA\Polls\Db\CommentMapper;
13+
use OCA\Polls\Db\Poll;
14+
use OCA\Polls\Db\PollMapper;
15+
use OCA\Polls\Service\CommentService;
16+
use OCA\Polls\Tests\Unit\UnitTestCase;
17+
use OCP\ISession;
18+
use OCP\Server;
19+
20+
class CommentServiceTest extends UnitTestCase {
21+
private CommentService $commentService;
22+
private CommentMapper $commentMapper;
23+
private PollMapper $pollMapper;
24+
private ISession $session;
25+
26+
private Poll $poll;
27+
28+
protected function setUp(): void {
29+
parent::setUp();
30+
$this->session = Server::get(ISession::class);
31+
$this->session->set('ncPollsUserId', 'admin');
32+
33+
$this->commentService = Server::get(CommentService::class);
34+
$this->commentMapper = Server::get(CommentMapper::class);
35+
$this->pollMapper = Server::get(PollMapper::class);
36+
37+
$poll = $this->fm->instance('OCA\Polls\Db\Poll');
38+
$poll->setOwner('admin');
39+
$this->poll = $this->pollMapper->insert($poll);
40+
}
41+
42+
protected function tearDown(): void {
43+
parent::tearDown();
44+
// Comments cascade-delete with the poll
45+
try {
46+
$this->pollMapper->delete($this->poll);
47+
} catch (\Exception) {
48+
}
49+
}
50+
51+
// --- add ---
52+
53+
public function testAddCreatesComment(): void {
54+
$comment = $this->commentService->add('Hello World', $this->poll->getId());
55+
$this->assertInstanceOf(Comment::class, $comment);
56+
$this->assertSame('Hello World', $comment->getComment());
57+
$this->assertSame('admin', $comment->getUserId());
58+
$this->assertSame($this->poll->getId(), $comment->getPollId());
59+
$this->assertSame(Comment::CONFIDENTIAL_NO, $comment->getConfidential());
60+
$this->assertNull($comment->getRecipient());
61+
}
62+
63+
public function testAddConfidentialComment(): void {
64+
$comment = $this->commentService->add('Secret', $this->poll->getId(), true);
65+
$this->assertSame(Comment::CONFIDENTIAL_YES, $comment->getConfidential());
66+
// recipient is set to the poll owner for confidential comments
67+
$this->assertSame('admin', $comment->getRecipient());
68+
}
69+
70+
// --- list ---
71+
72+
public function testListReturnsAddedComments(): void {
73+
$this->commentService->add('First', $this->poll->getId());
74+
$this->commentService->add('Second', $this->poll->getId());
75+
$comments = $this->commentService->list($this->poll->getId());
76+
$this->assertCount(2, $comments);
77+
}
78+
79+
public function testListGroupsRapidCommentsFromSameUser(): void {
80+
$first = $this->commentService->add('First', $this->poll->getId());
81+
$second = $this->commentService->add('Second', $this->poll->getId());
82+
$comments = $this->commentService->list($this->poll->getId());
83+
// Both posted within 5 minutes by the same user → second gets parent set
84+
$this->assertSame(0, $comments[0]->getParent());
85+
$this->assertSame($first->getId(), $comments[1]->getParent());
86+
}
87+
88+
public function testListDoesNotReturnDeletedComments(): void {
89+
$comment = $this->commentService->add('To delete', $this->poll->getId());
90+
$this->commentService->delete($comment->getId());
91+
$comments = $this->commentService->list($this->poll->getId());
92+
$this->assertEmpty($comments);
93+
}
94+
95+
// --- delete / restore ---
96+
97+
public function testDeleteSetsDeletedTimestamp(): void {
98+
$comment = $this->commentService->add('To delete', $this->poll->getId());
99+
$deleted = $this->commentService->delete($comment->getId());
100+
$this->assertGreaterThan(0, $deleted->getDeleted());
101+
}
102+
103+
public function testRestoreClearsDeletedTimestamp(): void {
104+
$comment = $this->commentService->add('To restore', $this->poll->getId());
105+
$this->commentService->delete($comment->getId());
106+
$restored = $this->commentService->restore($comment->getId());
107+
$this->assertSame(0, $restored->getDeleted());
108+
}
109+
110+
public function testOwnerCanDeleteForeignComment(): void {
111+
// Insert a comment directly as a different user
112+
$foreign = new Comment();
113+
$foreign->setPollId($this->poll->getId());
114+
$foreign->setUserId('other_user');
115+
$foreign->setComment('Foreign comment');
116+
$foreign->setTimestamp(time());
117+
$foreign->setConfidential(Comment::CONFIDENTIAL_NO);
118+
$foreign = $this->commentMapper->insert($foreign);
119+
120+
// Admin (poll owner) has PERMISSION_COMMENT_DELETE → can delete foreign comment
121+
$deleted = $this->commentService->delete($foreign->getId());
122+
$this->assertGreaterThan(0, $deleted->getDeleted());
123+
}
124+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Polls\Tests\Unit\Service;
10+
11+
use OCA\Polls\Db\Poll;
12+
use OCA\Polls\Db\PollMapper;
13+
use OCA\Polls\Db\Share;
14+
use OCA\Polls\Db\ShareMapper;
15+
use OCA\Polls\Exceptions\InvalidShareTypeException;
16+
use OCA\Polls\Service\ShareService;
17+
use OCA\Polls\Tests\Unit\UnitTestCase;
18+
use OCP\ISession;
19+
use OCP\Server;
20+
21+
class ShareServiceTest extends UnitTestCase {
22+
private ShareService $shareService;
23+
private ShareMapper $shareMapper;
24+
private PollMapper $pollMapper;
25+
private ISession $session;
26+
27+
private Poll $poll;
28+
private Share $userShare;
29+
30+
protected function setUp(): void {
31+
parent::setUp();
32+
$this->session = Server::get(ISession::class);
33+
$this->session->set('ncPollsUserId', 'admin');
34+
35+
$this->shareService = Server::get(ShareService::class);
36+
$this->shareMapper = Server::get(ShareMapper::class);
37+
$this->pollMapper = Server::get(PollMapper::class);
38+
39+
$poll = $this->fm->instance('OCA\Polls\Db\Poll');
40+
$poll->setOwner('admin');
41+
$this->poll = $this->pollMapper->insert($poll);
42+
43+
// Pre-create a TYPE_USER share for admin (used in setType tests)
44+
$share = $this->fm->instance('OCA\Polls\Db\Share');
45+
$share->setPollId($this->poll->getId());
46+
$share->setType(Share::TYPE_USER);
47+
$share->setUserId('admin');
48+
$this->userShare = $this->shareMapper->insert($share);
49+
}
50+
51+
protected function tearDown(): void {
52+
parent::tearDown();
53+
// Shares cascade-delete with the poll
54+
try {
55+
$this->pollMapper->delete($this->poll);
56+
} catch (\Exception) {
57+
}
58+
}
59+
60+
// --- add ---
61+
62+
public function testAddPublicShareCreatesShare(): void {
63+
$share = $this->shareService->add($this->poll->getId(), Share::TYPE_PUBLIC);
64+
$this->assertInstanceOf(Share::class, $share);
65+
$this->assertSame(Share::TYPE_PUBLIC, $share->getType());
66+
$this->assertSame($this->poll->getId(), $share->getPollId());
67+
$this->assertNotEmpty($share->getToken());
68+
}
69+
70+
public function testAddEmailShareCreatesShare(): void {
71+
$share = $this->shareService->add(
72+
$this->poll->getId(),
73+
Share::TYPE_EMAIL,
74+
'invited@polls.example.com',
75+
'Invited Person',
76+
'invited@polls.example.com',
77+
);
78+
$this->assertInstanceOf(Share::class, $share);
79+
$this->assertSame(Share::TYPE_EMAIL, $share->getType());
80+
$this->assertSame($this->poll->getId(), $share->getPollId());
81+
}
82+
83+
public function testAddDuplicatePublicShareReturnsSameToken(): void {
84+
$first = $this->shareService->add($this->poll->getId(), Share::TYPE_PUBLIC);
85+
$second = $this->shareService->add($this->poll->getId(), Share::TYPE_PUBLIC);
86+
// ShareAlreadyExistsException is caught internally; same share returned
87+
$this->assertSame($first->getToken(), $second->getToken());
88+
}
89+
90+
// --- get ---
91+
92+
public function testGetShareByToken(): void {
93+
$share = $this->shareService->get($this->userShare->getToken());
94+
$this->assertInstanceOf(Share::class, $share);
95+
$this->assertSame($this->userShare->getId(), $share->getId());
96+
}
97+
98+
// --- list ---
99+
100+
public function testListReturnsPollShares(): void {
101+
$shares = $this->shareService->list($this->poll->getId());
102+
$this->assertNotEmpty($shares);
103+
foreach ($shares as $share) {
104+
$this->assertSame($this->poll->getId(), $share->getPollId());
105+
}
106+
}
107+
108+
// --- delete / restore ---
109+
110+
public function testDeleteByTokenSetsDeletedTimestamp(): void {
111+
$deleted = $this->shareService->deleteByToken($this->userShare->getToken());
112+
$this->assertGreaterThan(0, $deleted->getDeleted());
113+
}
114+
115+
public function testRestoreByTokenClearsDeletedTimestamp(): void {
116+
$this->shareService->deleteByToken($this->userShare->getToken());
117+
$restored = $this->shareService->deleteByToken($this->userShare->getToken(), restore: true);
118+
$this->assertSame(0, $restored->getDeleted());
119+
}
120+
121+
// --- lock / unlock ---
122+
123+
public function testLockByTokenSetsLockedTimestamp(): void {
124+
$locked = $this->shareService->lockByToken($this->userShare->getToken());
125+
$this->assertGreaterThan(0, $locked->getLocked());
126+
}
127+
128+
public function testUnlockByTokenClearsLockedTimestamp(): void {
129+
$this->shareService->lockByToken($this->userShare->getToken());
130+
$unlocked = $this->shareService->lockByToken($this->userShare->getToken(), unlock: true);
131+
$this->assertSame(0, $unlocked->getLocked());
132+
}
133+
134+
// --- setType ---
135+
136+
public function testSetTypeChangesUserShareToAdmin(): void {
137+
$updated = $this->shareService->setType($this->userShare->getToken(), Share::TYPE_ADMIN);
138+
$this->assertSame(Share::TYPE_ADMIN, $updated->getType());
139+
}
140+
141+
public function testSetTypeChangesAdminShareToUser(): void {
142+
// Promote to admin first, then demote back
143+
$this->shareService->setType($this->userShare->getToken(), Share::TYPE_ADMIN);
144+
$updated = $this->shareService->setType($this->userShare->getToken(), Share::TYPE_USER);
145+
$this->assertSame(Share::TYPE_USER, $updated->getType());
146+
}
147+
148+
public function testSetTypeRejectsInvalidTransition(): void {
149+
// Only user↔admin transitions are supported; public→admin must not change
150+
$publicShare = $this->shareService->add($this->poll->getId(), Share::TYPE_PUBLIC);
151+
$result = $this->shareService->setType($publicShare->getToken(), Share::TYPE_ADMIN);
152+
// setType silently returns the unchanged share when transition is not allowed
153+
$this->assertSame(Share::TYPE_PUBLIC, $result->getType());
154+
}
155+
156+
// --- setEmailAddress ---
157+
158+
public function testSetEmailAddressThrowsForNonExternalShare(): void {
159+
$this->expectException(InvalidShareTypeException::class);
160+
$this->shareService->setEmailAddress($this->userShare, 'new@example.com');
161+
}
162+
}

0 commit comments

Comments
 (0)