summaryrefslogtreecommitdiffstats
path: root/tests/Inc/UserTest.php
blob: e9ca22106d5de0f9c061d89661ec229e626cc6f8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?php

use PHPUnit\Framework\TestCase;

/**
 * Demonstrates how to test a real class when a stub exists: opt out of the stub
 * via the stub autoloader allowlist and let the normal inc/ autoloader load the
 * production class instead. Now adapted to use the SQLite-backed Database test
 * backend so we assert against real SQL, not stub call logs.
 *
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
class UserTest extends TestCase
{
	protected function setUp(): void
	{
		$GLOBALS['__TEST_USE_REAL_CLASSES'] = ['User'];

		Database::resetSchema();
		Session::reset();
	}

	public function testBasicAccessorsLoggedOutAndLoggedIn(): void
	{
		// Sanity: manipulate private static User::$user via reflection
		$ref = new ReflectionClass('User');
		$prop = $ref->getProperty('user');
		$prop->setAccessible(true);

		// Start logged out
		$prop->setValue(null, false);
		$this->assertFalse(User::isLoggedIn());
		$this->assertNull(User::getId());
		$this->assertFalse(User::getName());
		$this->assertNull(User::getLogin());

		// Now set a minimal user record
		$user = [
			'userid' => 42,
			'fullname' => 'Alice Doe',
			'login' => 'alice',
			'permissions' => 0,
			'lasteventid' => null,
		];
		$prop->setValue(null, $user);

		$this->assertTrue(User::isLoggedIn());
		$this->assertSame(42, User::getId());
		$this->assertSame('Alice Doe', User::getName());
		$this->assertSame('alice', User::getLogin());
	}

	public function testSetAndGetLastSeenEventUpdatesDatabaseAndMemory(): void
	{
		$ref = new ReflectionClass('User');
		$prop = $ref->getProperty('user');
		$prop->setAccessible(true);
		// Use an existing seeded user (id=1)
		$prop->setValue(null, [
			'userid' => 1,
			'fullname' => 'Alice Doe',
			'login' => 'alice',
			'permissions' => 0,
			'lasteventid' => 1,
		]);

		User::setLastSeenEvent(1234);

		// Assert DB has been updated
		$row = Database::queryFirst('SELECT lasteventid FROM user WHERE userid = :u', ['u' => 1]);
		$this->assertNotFalse($row);
		$this->assertSame(1234, (int)$row['lasteventid']);

		// And memory updated too
		$this->assertSame(1234, User::getLastSeenEvent());
	}

	public function testUpdatePasswordUsesCryptoAndExecutesUpdate(): void
	{
		$ref = new ReflectionClass('User');
		$prop = $ref->getProperty('user');
		$prop->setAccessible(true);
		// Use seeded user id=1
		$prop->setValue(null, [
			'userid' => 1,
			'fullname' => 'Alice Doe',
			'login' => 'alice',
			'permissions' => 0,
			'lasteventid' => null,
		]);

		// Act
		$ret = User::updatePassword('secret');
		$this->assertTrue($ret);

		// Assert DB updated with hashed password from Crypto stub
		$row = Database::queryFirst('SELECT passwd FROM user WHERE userid = :u', ['u' => 1]);
		$this->assertNotFalse($row);
		$this->assertSame('HASHED-secret', $row['passwd']);
	}

	public function testLoginVerifiesPasswordAndCreatesSession(): void
	{
		$_SERVER['REMOTE_ADDR'] = '192.168.127.12';
		$_SERVER['REMOTE_PORT'] = 12345;
		$_SERVER['HTTP_USER_AGENT'] = 'foobar';
		Session::reset();

		// Good password (Crypto::verify will accept 'ok' against 'STORED' from seed)
		$this->assertTrue(User::login('alice', 'ok', true));
		$this->assertNotNull(Session::$lastCreate);
		$this->assertSame(1, Session::$lastCreate['userId']);

		// Bad password
		Session::reset();
		$this->assertFalse(User::login('alice', 'bad', false));
		$this->assertNull(Session::$lastCreate);
	}
}