Skip to content

Commit c4ba44f

Browse files
committed
WIP unit tests for tagging
1 parent dfe006f commit c4ba44f

9 files changed

+231
-25
lines changed

classes/local/manager.php

+5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public static function get_objectfs_config() {
6464
$config->batchsize = 10000;
6565
$config->useproxy = 0;
6666
$config->deleteexternal = 0;
67+
$config->enabletagging = false;
6768

6869
$config->filesystem = '';
6970
$config->enablepresignedurls = 0;
@@ -329,6 +330,10 @@ public static function get_available_fs_list() {
329330
* @return string
330331
*/
331332
public static function get_client_classname_from_fs($filesystem) {
333+
// Unit tests.
334+
if($filesystem == '\tool_objectfs\tests\test_file_system') {
335+
return '\tool_objectfs\tests\test_client';
336+
}
332337
$clientclass = str_replace('_file_system', '', $filesystem);
333338
return str_replace('tool_objectfs\\', 'tool_objectfs\\local\\store\\', $clientclass.'\\client');
334339
}

classes/local/store/object_file_system.php

-4
Original file line numberDiff line numberDiff line change
@@ -1154,8 +1154,4 @@ private function update_object(array $result): array {
11541154

11551155
return $result;
11561156
}
1157-
1158-
public function update_external_object_tags($contenthash, $tags) {
1159-
return;
1160-
}
11611157
}

classes/local/tag/file_type_source.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class file_type_source implements tag_source {
77

88
public const TYPE_BACKUP = 'backup';
99

10-
public static function get_identifer(): string {
10+
public static function get_identifier(): string {
1111
return 'type';
1212
}
1313

classes/local/tag/tag_manager.php

+4-5
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ public static function get_defined_tag_sources(): array {
3434
*/
3535
public static function is_tagging_enabled(): bool {
3636
$enabledinconfig = !empty(get_config('tool_objectfs', 'taggingenabled'));
37+
3738
$client = manager::get_client(manager::get_objectfs_config());
38-
$supportedbyfs = $client->supports_object_tagging();
39+
$supportedbyfs = !empty($client) && $client->supports_object_tagging();
3940

4041
return $enabledinconfig && $supportedbyfs;
4142
}
@@ -55,7 +56,7 @@ private static function gather_tags(string $contenthash): array {
5556
continue;
5657
}
5758

58-
$tags[$source->get_identifer()] = $val;
59+
$tags[$source->get_identifier()] = $val;
5960
}
6061
return $tags;
6162
}
@@ -150,8 +151,6 @@ public static function get_objects_needing_sync(int $limit) {
150151
}
151152

152153
public static function replicate_local_to_external_tags_for_object(string $contenthash) {
153-
global $DB;
154-
155154
// Take lock, to ensure tags are not updated while we sync them.
156155
$lock = self::get_object_tag_lock($contenthash);
157156

@@ -189,6 +188,6 @@ private static function mark_object_tag_status(string $contenthash, int $status)
189188
}
190189

191190
private static function are_tags_different(array $a, array $b) {
192-
return true; // TODO compare the keys and values to ensure they are exactly the same!
191+
return $a !== $b;
193192
}
194193
}

classes/local/tag/tag_source.php

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
namespace tool_objectfs\local\tag;
44

5-
use stored_file;
6-
75
interface tag_source {
86
/**
97
* Returns an unchanging identifier for this source.
@@ -12,7 +10,7 @@ interface tag_source {
1210
* Must not exceed 128 chars. TODO unit test this.
1311
* @return string
1412
*/
15-
public static function get_identifer(): string;
13+
public static function get_identifier(): string;
1614

1715
/**
1816
* Returns the value of this tag for the file with the given content hash.

classes/task/update_object_tags.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
class update_object_tags extends adhoc_task {
99
public function execute() {
1010
if (!tag_manager::is_tagging_enabled()) {
11-
mtrace("Tagging feature not available or supported, ignoring.");
11+
mtrace("Tagging feature not enabled or supported by filesystem, exiting early.");
1212
return;
1313
}
1414

15-
// Find the object ids from the customdata.
15+
// Find the object from the customdata.
1616
$contenthash = $this->get_custom_data()->contenthash ?? null;
1717

1818
if (empty($contenthash)) {

classes/tests/test_client.php

+5
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,10 @@ public function test_permissions($testdelete) {
157157
public function get_maximum_upload_size() {
158158
return $this->maxupload;
159159
}
160+
161+
public function supports_object_tagging(): bool {
162+
global $CFG;
163+
return $CFG->phpunit_objectfs_supports_object_tagging;
164+
}
160165
}
161166

classes/tests/test_tagging.php

-10
This file was deleted.

tests/local/tagging_test.php

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
<?php
2+
3+
namespace tool_objectfs\local;
4+
5+
use coding_exception;
6+
use tool_objectfs\local\manager;
7+
use tool_objectfs\local\tag\tag_manager;
8+
use tool_objectfs\local\tag\tag_source;
9+
use tool_objectfs\tests\testcase;
10+
11+
class tagging_test extends testcase {
12+
public function test_get_defined_tag_sources() {
13+
$sources = tag_manager::get_defined_tag_sources();
14+
$this->assertIsArray($sources);
15+
16+
// Both AWS and Azure limit 10 tags per object, so ensure never more than 10 sources defined.
17+
$this->assertLessThanOrEqual(10, count($sources));
18+
}
19+
20+
public static function tag_source_provider(): array {
21+
$sources = tag_manager::get_defined_tag_sources();
22+
$tests = [];
23+
24+
foreach ($sources as $source) {
25+
$tests[$source->get_identifier()] = [
26+
'source' => $source
27+
];
28+
}
29+
30+
return $tests;
31+
}
32+
33+
/**
34+
* @dataProvider tag_source_provider
35+
*/
36+
public function test_tag_sources_identifier(tag_source $source) {
37+
// Ensure tag source is < 128 chars, to fit AWS & Azure spec.
38+
$count = strlen($source->get_identifier());
39+
$this->assertLessThan(128, $count);
40+
$this->assertGreaterThan(0, $count);
41+
}
42+
43+
public static function is_tagging_enabled_provider(): array {
44+
return [
45+
'neither config nor fs supports' => [
46+
'enabledinconfig' => false,
47+
'supportedbyfs' => false,
48+
'expected' => false,
49+
],
50+
'enabled in config but fs does not support' => [
51+
'enabledinconfig' => true,
52+
'supportedbyfs' => false,
53+
'expected' => false,
54+
],
55+
'enabled in config and fs does support' => [
56+
'enabledinconfig' => true,
57+
'supportedbyfs' => true,
58+
'expected' => true,
59+
],
60+
];
61+
}
62+
63+
/**
64+
* @dataProvider is_tagging_enabled_provider
65+
*/
66+
public function test_is_tagging_enabled(bool $enabledinconfig, bool $supportedbyfs, bool $expected) {
67+
global $CFG;
68+
// Set config.
69+
set_config('taggingenabled', $enabledinconfig, 'tool_objectfs');
70+
71+
// Set supported by fs.
72+
$config = manager::get_objectfs_config();
73+
$config->taggingenabled = $enabledinconfig;
74+
$config->enabletasks = true;
75+
$config->filesystem = '\\tool_objectfs\\tests\\test_file_system';
76+
manager::set_objectfs_config($config);
77+
$CFG->phpunit_objectfs_supports_object_tagging = $supportedbyfs;
78+
79+
$this->assertEquals($expected, tag_manager::is_tagging_enabled());
80+
}
81+
82+
public function test_query_local_tags() {
83+
global $DB;
84+
85+
// Setup some fake tag data.
86+
$DB->insert_records('tool_objectfs_object_tags', [
87+
[
88+
'contenthash' => 'abc123',
89+
'tagkey' => 'test',
90+
'tagvalue' => 'test',
91+
'timemodified' => time()
92+
],
93+
[
94+
'contenthash' => 'abc123',
95+
'tagkey' => 'test2',
96+
'tagvalue' => 'test2',
97+
'timemodified' => time()
98+
]
99+
]);
100+
101+
$this->assertCount(2, tag_manager::query_local_tags('abc123'));
102+
$this->assertCount(0, tag_manager::query_local_tags('doesnotexist'));
103+
}
104+
105+
public function test_update_tags_if_necessary() {
106+
global $DB;
107+
108+
// There should be no existing tags to begin with.
109+
$this->assertEmpty(tag_manager::query_local_tags('test'));
110+
111+
// Update tags if necessary - should update the tags.
112+
tag_manager::update_tags_if_necessary('test');
113+
114+
// Query tags - should be equal to number defined by the manager.
115+
$expectedcount = count(tag_manager::get_defined_tag_sources());
116+
$this->assertCount($expectedcount, tag_manager::query_local_tags('test'), 'Tags created match the number defined by manager');
117+
118+
// Note the timemodified of one of the tags.
119+
$timemodified = $DB->get_field('tool_objectfs_object_tags', 'timemodified', ['contenthash' => 'test', 'tagkey' => tag_manager::get_defined_tag_sources()[0]->get_identifier()]);
120+
121+
// Running again, timemodified should not change.
122+
$this->waitForSecond();
123+
tag_manager::update_tags_if_necessary('test');
124+
125+
$newtimemodified = $DB->get_field('tool_objectfs_object_tags', 'timemodified', ['contenthash' => 'test', 'tagkey' => tag_manager::get_defined_tag_sources()[0]->get_identifier()]);
126+
$this->assertEquals($timemodified, $newtimemodified, 'Tags timemodified must not change if not forced and values are not different');
127+
128+
// Except if it is forced.
129+
$this->waitForSecond();
130+
tag_manager::update_tags_if_necessary('test', true);
131+
132+
$timemodifiedafterforce = $DB->get_field('tool_objectfs_object_tags', 'timemodified', ['contenthash' => 'test', 'tagkey' => tag_manager::get_defined_tag_sources()[0]->get_identifier()]);
133+
$this->assertNotEquals($timemodified, $timemodifiedafterforce, 'Forced tag update changed time modified');
134+
}
135+
136+
public static function get_objects_needing_sync_provider(): array {
137+
return [
138+
'duplicated, needs sync' => [
139+
'location' => OBJECT_LOCATION_DUPLICATED,
140+
'status' => tag_manager::SYNC_STATUS_NEEDS_SYNC,
141+
'expectedneedssync' => true,
142+
],
143+
'remote, needs sync' => [
144+
'location' => OBJECT_LOCATION_EXTERNAL,
145+
'status' => tag_manager::SYNC_STATUS_NEEDS_SYNC,
146+
'expectedneedssync' => true,
147+
],
148+
'local, needs sync' => [
149+
'location' => OBJECT_LOCATION_LOCAL,
150+
'status' => tag_manager::SYNC_STATUS_NEEDS_SYNC,
151+
'expectedneedssync' => false,
152+
],
153+
'duplicated, does not need sync' => [
154+
'location' => OBJECT_LOCATION_DUPLICATED,
155+
'status' => tag_manager::SYNC_STATUS_SYNC_NOT_REQUIRED,
156+
'expectedneedssync' => false,
157+
],
158+
'local, does not need sync' => [
159+
'location' => OBJECT_LOCATION_LOCAL,
160+
'status' => tag_manager::SYNC_STATUS_SYNC_NOT_REQUIRED,
161+
'expectedneedssync' => false,
162+
],
163+
];
164+
}
165+
166+
/**
167+
* @dataProvider get_objects_needing_sync_provider
168+
*/
169+
public function test_get_objects_needing_sync(int $location, int $syncstatus, bool $expectedneedssync) {
170+
global $DB;
171+
172+
// Create the test object at the required location.
173+
switch ($location) {
174+
case OBJECT_LOCATION_DUPLICATED:
175+
$object = $this->create_duplicated_object();
176+
break;
177+
case OBJECT_LOCATION_LOCAL:
178+
$object = $this->create_local_object();
179+
break;
180+
case OBJECT_LOCATION_EXTERNAL:
181+
$object = $this->create_remote_object();
182+
break;
183+
default:
184+
throw new coding_exception("Object location not handled in test");
185+
}
186+
187+
// Set the sync status.
188+
$DB->set_field('tool_objectfs_objects', 'tagsyncstatus', $syncstatus, ['id' => $object->id]);
189+
190+
// Check if it is included in the list.
191+
$needssync = tag_manager::get_objects_needing_sync(1);
192+
193+
if ($expectedneedssync) {
194+
$this->assertContains($object->contenthash, $needssync);
195+
} else {
196+
$this->assertNotContains($object->contenthash, $needssync);
197+
}
198+
}
199+
200+
public function test_get_objects_needing_sync_limit() {
201+
global $DB;
202+
203+
// Create two duplicated objects needing sync.
204+
$object = $this->create_duplicated_object();
205+
$DB->set_field('tool_objectfs_objects', 'tagsyncstatus', tag_manager::SYNC_STATUS_NEEDS_SYNC, ['id' => $object->id]);
206+
$object = $this->create_remote_object();
207+
$DB->set_field('tool_objectfs_objects', 'tagsyncstatus', tag_manager::SYNC_STATUS_NEEDS_SYNC, ['id' => $object->id]);
208+
209+
// Ensure a limit of 2 returns 2, and limit of 1 returns 1.
210+
$this->assertCount(2, tag_manager::get_objects_needing_sync(2));
211+
$this->assertCount(1, tag_manager::get_objects_needing_sync(1));
212+
}
213+
}

0 commit comments

Comments
 (0)