Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
25.00% covered (danger)
25.00%
26 / 104
33.33% covered (danger)
33.33%
4 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
TestEnvironment
25.00% covered (danger)
25.00%
26 / 104
33.33% covered (danger)
33.33%
4 / 12
436.42
0.00% covered (danger)
0.00%
0 / 1
 setup
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
56
 makeRandomStatic
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setUpVirtualFilesystem
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 createTestFiles
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 createTestData
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 overwriteModificationDatesVfs
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 buildTestDB
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 updateDataBaseScheme
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 rollback
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 bailOut
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 defineTestDataDir
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 overwriteModificationDatesTestDataDir
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3declare(strict_types=1);
4
5use JetBrains\PhpStorm\NoReturn;
6use org\bovigo\vfs\vfsStream;
7use org\bovigo\vfs\vfsStreamContent;
8use org\bovigo\vfs\vfsStreamWrapper;
9
10class TestEnvironment {
11  const int staticDate = 1627545600;
12  const array testModes = ['prepare', 'api', 'integration', 'prepare-integration'];
13  static string | null $testMode = null;
14
15
16  public static function setup(string $testMode, ?string $testClock = null): void {
17    self::$testMode = in_array($testMode, self::testModes) ? $testMode : 'api';
18    $testClock = $testClock ?? self::staticDate;
19
20    try {
21      SystemConfig::$debug_useStaticTime = '@' . (int) $testClock;
22      SystemConfig::$debug_useStaticTokens = true;
23      SystemConfig::$debug_useInsecurePasswords = true;
24      SystemConfig::$debug_allowExternalXmlSchema = false;
25      SystemConfig::$debug_fastLoginReuse = true;
26      self::makeRandomStatic();
27      DB::connectToTestDB();
28
29      if (self::$testMode == 'integration') {
30        // this is called every single call from integration tests
31        self::defineTestDataDir(false);
32      }
33
34      if (self::$testMode == 'prepare-integration') {
35        // this is called one time before each integration test (cypress)
36        self::defineTestDataDir(true);
37        self::createTestFiles(true);
38        self::overwriteModificationDatesTestDataDir();
39        self::buildTestDB();
40        self::createTestData();
41      }
42
43      if (self::$testMode == 'prepare') {
44        // this is called once before the api tests (dredd)
45        self::setUpVirtualFilesystem();
46        self::createTestFiles(false);
47        self::overwriteModificationDatesVfs();
48        self::buildTestDB();
49        self::createTestData();
50      }
51
52      if (self::$testMode == 'api') {
53        // api tests can use vfs for more speed
54        self::setUpVirtualFilesystem();
55        self::createTestFiles(false);
56        self::overwriteModificationDatesVfs();
57        // in api-tests every call is atomic and the test db gets restored afterwards
58        // the test db must be set up before with $testMode == 'prepare'
59        $initDAO = new InitDAO();
60        $initDAO->beginTransaction();
61        register_shutdown_function([self::class, "rollback"]);
62      }
63    } catch (Throwable $exception) {
64      TestEnvironment::bailOut($exception);
65    }
66  }
67
68  public static function makeRandomStatic(): void {
69    srand(1);
70  }
71
72  private static function setUpVirtualFilesystem(): void {
73    $vfs = vfsStream::setup('root', 0777);
74    vfsStream::newDirectory('data', 0777)->at($vfs);
75    vfsStream::newDirectory('data/ws_1', 0777)->at($vfs);
76
77    define('DATA_DIR', vfsStream::url('root/data'));
78  }
79
80  private static function createTestFiles(bool $includeSystemTestFiles): void {
81    $initializer = new WorkspaceInitializer();
82    $initializer->importSampleFiles(1, 'default');
83    Folder::createPath(DATA_DIR . "/ws_1/UnitAttachments");
84    $initializer->createSampleScanImage("UnitAttachments/h5ki-bd-va4dg-jc2to2mp_6tga4teiw.png", 1);
85    if ($includeSystemTestFiles) {
86      $initializer->importSampleFiles(1, 'system-test');
87      $initializer->importSampleFiles(2, 'default');
88    }
89  }
90
91  private static function createTestData(): void {
92    $initDAO = new InitDAO();
93
94    $initDAO->createWorkspace('sample_workspace');
95    $initDAO->createWorkspace('second_workspace');
96
97    $adminId = $initDAO->createAdmin('super', 'user123');
98    $initDAO->addWorkspacesToAdmin($adminId, [1, 2]);
99
100    (new Workspace(1))->storeAllFiles();
101    (new Workspace(2))->storeAllFiles();
102
103    $initDAO->createSampleLoginsReviewsLogs();
104    $initDAO->createSampleExpiredSessions();
105    $initDAO->createSampleWorkspaceAdmins();
106    $initDAO->createSampleMetaData();
107    $personSessions = $initDAO->createSampleMonitorSessions();
108    $groupMonitor = $personSessions['test-group-monitor'];
109    /* @var $groupMonitor PersonSession */
110    $initDAO->createSampleCommands($groupMonitor->getPerson()->getId());
111
112    $initializer = new WorkspaceInitializer();
113    $initializer->createSampleScanImage('sample_scanned_image.png', 1);
114    $initDAO->importScanImage(1, 'sample_scanned_image.png');
115  }
116
117  public static function overwriteModificationDatesVfs(vfsStreamContent $dir = null): void {
118    if (!$dir) {
119      $dir = vfsStreamWrapper::getRoot()->getChild('data');
120    }
121    $dir->lastModified(TestEnvironment::staticDate);
122    foreach ($dir->getChildren() as $child) {
123      $child->lastModified(TestEnvironment::staticDate);
124      if (is_dir($child->url())) {
125        TestEnvironment::overwriteModificationDatesVfs($child);
126      }
127    }
128  }
129
130  static function buildTestDB(): void {
131    $initDAO = new InitDAO();
132    $nextPatchPath = ROOT_DIR . '/scripts/database/patches.d/next.sql';
133    $fullSchemePath = ROOT_DIR . '/scripts/database/full.sql';
134    $patchFileChanged = (file_exists($nextPatchPath) and (filemtime($nextPatchPath) > filemtime($fullSchemePath)));
135
136    if (!file_exists($fullSchemePath) or $patchFileChanged) {
137      TestEnvironment::updateDataBaseScheme();
138      return;
139    }
140    $initDAO->clearDB();
141    $initDAO->runFile(ROOT_DIR . '/scripts/database/full.sql');
142  }
143
144  private static function updateDataBaseScheme(): void {
145    $initDAO = new InitDAO();
146    $initDAO->clearDB();
147    $initDAO->runFile(ROOT_DIR . "/scripts/database/base.sql");
148    $initDAO->installPatches(ROOT_DIR . "/scripts/database/patches.d", false);
149
150    $scheme = '-- IQB-Testcenter DB --';
151    foreach ($initDAO::tables as $table) {
152      $scheme .= "\n\n" . $initDAO->_("show create table $table")['Create Table'] . ";";
153      $scheme .= "\n" . "truncate $table; -- to reset auto-increment";
154    }
155    file_put_contents(ROOT_DIR . '/scripts/database/full.sql', $scheme);
156  }
157
158  private static function rollback(): void {
159    $initDAO = new InitDAO();
160    $initDAO->rollBack();
161  }
162
163  #[NoReturn]
164  private static function bailOut(Throwable $exception): void {
165    // TestEnvironment::debugVirtualEnvironment();
166    $errorUniqueId = ErrorHandler::logException($exception, true);
167    http_response_code(500);
168    header("Error-ID:$errorUniqueId");
169    throw new RuntimeException("Could not create environment: " . $exception->getMessage());
170  }
171
172  private static function defineTestDataDir(bool $shouldReset): void {
173    define('DATA_DIR', ROOT_DIR . '/data-TEST');
174    if (!$shouldReset) {
175      return;
176    }
177    Folder::createPath(DATA_DIR);
178    Folder::deleteContentsRecursive(DATA_DIR);
179  }
180
181  private static function overwriteModificationDatesTestDataDir(?string $dir = DATA_DIR): void {
182    touch($dir, TestEnvironment::staticDate);
183    foreach (new DirectoryIterator($dir) as $child) {
184      if ($child->isDot() or $child->isLink()) {
185        continue;
186      }
187      touch($child->getPathname(), TestEnvironment::staticDate);
188      if ($child->isDir()) {
189        self::overwriteModificationDatesTestDataDir($child->getPathname());
190      }
191    }
192  }
193}