$field */
+ $this->populateViolations($field, $fieldSchema['properties'], $violations, $path);
+ continue;
+ }
+
+ /** @var string $errorCode */
+ $errorCode = $error->get_error_code();
+
+ $violations->offsetSet(
+ $path,
+ Violation::new(
+ $path,
+ $error->get_error_message(),
+ $errorCode,
+ )
+ );
+ }
+ }
+
+ public function publishSchema(): array
+ {
+ return $this->publishSchema;
+ }
+
+ public function draftSchema(): array
+ {
+ return $this->draftSchema;
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/bootstrap.php b/vendor/rrze/fau-studium-common/tests/bootstrap.php
new file mode 100644
index 0000000..c40fca3
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/bootstrap.php
@@ -0,0 +1,13 @@
+fixtureData();
+ $sut = new ConditionalFieldsValidator(
+ new FacultyRepository(),
+ );
+ $result = $sut->validatePublish($data);
+ $this->assertSame($result->count(), 0);
+ }
+
+ public function testEmptyDegreeProgramFees(): void
+ {
+ $data = $this->fixtureData();
+ $data[DegreeProgram::DEGREE_PROGRAM_FEES] = MultilingualString::empty()->asArray();
+ $sut = new ConditionalFieldsValidator(
+ new FacultyRepository(),
+ );
+ $violations = $sut->validatePublish($data);
+ $this->assertSame($violations->count(), 2);
+ $this->assertSame([
+ 'degree_program_fees.de' => [
+ 'path' => 'degree_program_fees.de',
+ 'errorMessage' => 'This field can not be empty.',
+ 'errorCode' => 'rest_too_short',
+ ],
+ 'degree_program_fees.en' => [
+ 'path' => 'degree_program_fees.en',
+ 'errorMessage' => 'This field can not be empty.',
+ 'errorCode' => 'rest_too_short',
+ ],
+ ], $violations->asArray());
+ }
+
+ public function testEmptyMasterAdmissionRequirement(): void
+ {
+ $data = $this->fixtureData();
+ $data[DegreeProgram::DEGREE][Degree::ABBREVIATION][MultilingualString::DE] = 'MA';
+ $data[DegreeProgram::ADMISSION_REQUIREMENTS][AdmissionRequirements::MASTER] = AdmissionRequirement::empty()->asArray();
+ $sut = new ConditionalFieldsValidator(
+ new FacultyRepository(),
+ );
+ $violations = $sut->validatePublish($data);
+ $this->assertSame($violations->count(), 1);
+ $this->assertSame([
+ 'admission_requirements.master' => [
+ 'path' => 'admission_requirements.master',
+ 'errorMessage' => 'This field can not be empty.',
+ 'errorCode' => 'rest_too_short',
+ ],
+ ], $violations->asArray());
+ }
+
+ public function testSkipValidationOnAdditionalDegree(): void
+ {
+ $data = $this->fixtureData();
+ $data[DegreeProgram::DEGREE][Degree::NAME][MultilingualString::DE] = 'Weiteres';
+ $data[DegreeProgram::ADMISSION_REQUIREMENTS][AdmissionRequirements::BACHELOR_OR_TEACHING_DEGREE] = AdmissionRequirement::empty()->asArray();
+ $sut = new ConditionalFieldsValidator(
+ new FacultyRepository(),
+ );
+ $violations = $sut->validatePublish($data);
+ $this->assertSame($violations->count(), 0);
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/Logger/WordPressLoggerTest.php b/vendor/rrze/fau-studium-common/tests/functional/Logger/WordPressLoggerTest.php
new file mode 100644
index 0000000..f9ecda6
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/Logger/WordPressLoggerTest.php
@@ -0,0 +1,106 @@
+tmpStream = tmpfile();
+ $this->oldErrorLogValue = ini_set(
+ 'error_log',
+ stream_get_meta_data($this->tmpStream)['uri']
+ );
+ define('WP_DEBUG_LOG', true);
+ parent::setUp();
+
+ $this->wpOption->addOption('home', 'https://fau.de');
+ }
+
+ public function tearDown(): void
+ {
+ ini_set('error_log', $this->oldErrorLogValue);
+ parent::tearDown();
+ }
+
+ public function testWordPressHook(): void
+ {
+ add_action('rrze.log.error', static function (string $message) {
+ self::assertSame('Error happens!', $message);
+ });
+
+ $sut = WordPressLogger::new(self::PACKAGE);
+ $sut->error('Error happens!');
+ $sut->info('Some info.');
+ self::assertSame(1, did_action('rrze.log.error'));
+ self::assertSame(1, did_action('rrze.log.info'));
+ }
+
+ public function testLogEntry(): void
+ {
+ $sut = WordPressLogger::new(self::PACKAGE);
+ $sut->error('Error happens!');
+ self::assertStringEndsWith(
+ "[ERROR] [my_package]: Error happens!\n{\"site_url\":\"https:\/\/fau.de\"}\n",
+ stream_get_contents($this->tmpStream)
+ );
+ }
+
+ /**
+ * phpcs:disable Inpsyde.CodeQuality.LineLength.TooLong
+ */
+ public function testContext(): void
+ {
+ add_action('rrze.log.error', static function (string $message, array $context) {
+ self::assertSame('here', $context['where']);
+ self::assertSame(self::PACKAGE, $context['plugin']);
+ }, 10, 2);
+
+ $sut = WordPressLogger::new(self::PACKAGE);
+ $sut->error('Error happens!', ['where' => 'here']);
+ self::assertStringEndsWith(
+ "[ERROR] [my_package]: Error happens!\n{\"where\":\"here\",\"site_url\":\"https:\/\/fau.de\"}\n",
+ stream_get_contents($this->tmpStream)
+ );
+ }
+
+ public function testException(): void
+ {
+ $exception = new Exception('Error!');
+
+ add_action('rrze.log.error', static function (string $message, array $context) {
+ self::assertSame('Error happens!', $message);
+ self::assertInstanceOf(Exception::class, $context['exception']);
+ self::assertSame('Error!', $context['exception']->getMessage());
+ }, 10, 2);
+
+ $sut = WordPressLogger::new(self::PACKAGE);
+ $sut->error('Error happens!', ['exception' => $exception]);
+ $entries = explode("\n", stream_get_contents($this->tmpStream));
+ self::assertStringEndsWith(
+ "[ERROR] [my_package]: Error happens!",
+ $entries[0]
+ );
+ self::assertStringEndsWith(
+ "Error!",
+ $entries[1]
+ );
+ self::assertStringEndsWith(
+ "Fau\DegreeProgram\Common\Tests\Logger\WordPressLoggerTest->testException()",
+ $entries[2]
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/Repository/IdGeneratorTest.php b/vendor/rrze/fau-studium-common/tests/functional/Repository/IdGeneratorTest.php
new file mode 100644
index 0000000..0c813a0
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/Repository/IdGeneratorTest.php
@@ -0,0 +1,175 @@
+ 5]);
+ $sut = new IdGenerator();
+
+ $this->assertSame(
+ 'term:5',
+ $sut->generateTermId($term)
+ );
+ $this->assertSame(
+ 'term:5:name',
+ $sut->generateTermId($term, 'name')
+ );
+ $this->assertSame(
+ 'term_meta:5:abbreviation',
+ $sut->generateTermMetaId($term, 'abbreviation')
+ );
+ }
+
+ public function testGenerateIdsForPost(): void
+ {
+ $post = new WP_Post((object) ['ID' => 5]);
+ $sut = new IdGenerator();
+
+ $this->assertSame(
+ 'post:5',
+ $sut->generatePostId($post)
+ );
+ $this->assertSame(
+ 'post:5:title',
+ $sut->generatePostId($post, 'title')
+ );
+ $this->assertSame(
+ 'post_meta:5:meta_description',
+ $sut->generatePostMetaId($post, 'meta_description')
+ );
+ }
+
+ public function testGenerateIdsForOption(): void
+ {
+ $optionKey = 'fau_start_of_semester';
+ $sut = new IdGenerator();
+
+ $this->assertSame(
+ 'option:fau_start_of_semester',
+ $sut->generateOptionId($optionKey)
+ );
+ $this->assertSame(
+ 'option:fau_start_of_semester:link_text',
+ $sut->generateOptionId($optionKey, 'link_text')
+ );
+ }
+
+ public function testParseId(): void
+ {
+ $sut = new IdGenerator();
+ [
+ 'type' => $type,
+ 'entityId' => $parsedPostId,
+ 'subField' => $key,
+ ] = $sut->parseId('post:5');
+ $this->assertSame('post', $type);
+ $this->assertSame('5', $parsedPostId);
+ $this->assertNull($key);
+
+ [
+ 'type' => $type,
+ 'entityId' => $parsedPostId,
+ 'subField' => $key,
+ ] = $sut->parseId('post_meta:5:meta_description');
+ $this->assertSame('post_meta', $type);
+ $this->assertSame('5', $parsedPostId);
+ $this->assertSame('meta_description', $key);
+ }
+
+ /**
+ * @dataProvider termListIdsProvider
+ */
+ public function testTermListIds(
+ MultilingualString|MultilingualList|MultilingualLink|MultilingualLinks|NumberOfStudents|Degree $valueObject,
+ array $ids
+ ): void {
+
+ $sut = new IdGenerator();
+ $this->assertSame($ids, $sut->termIdsList($valueObject));
+ }
+
+ public function termListIdsProvider(): iterable
+ {
+ yield 'multilingual_string' => [
+ MultilingualString::fromTranslations('term:25:name', '', ''),
+ [25],
+ ];
+
+ yield 'multilingual_list' => [
+ MultilingualList::new(
+ MultilingualString::fromTranslations('term:25:name', '', ''),
+ MultilingualString::fromTranslations('term:26:name', '', ''),
+ ),
+ [25, 26],
+ ];
+
+ yield 'multilingual_link' => [
+ MultilingualLink::new(
+ 'term:25',
+ MultilingualString::fromTranslations('term:25:name', '', ''),
+ MultilingualString::fromTranslations('term_meta:25:link_text', '', ''),
+ MultilingualString::fromTranslations('term_meta:25:link_url', '', ''),
+ ),
+ [25],
+ ];
+
+ yield 'multilingual_links' => [
+ MultilingualLinks::new(
+ MultilingualLink::new(
+ 'term:25',
+ MultilingualString::fromTranslations('term:25:name', '', ''),
+ MultilingualString::fromTranslations('term_meta:25:link_text', '', ''),
+ MultilingualString::fromTranslations('term_meta:25:link_url', '', ''),
+ ),
+ MultilingualLink::new(
+ 'term:27',
+ MultilingualString::fromTranslations('term:27:name', '', ''),
+ MultilingualString::fromTranslations('term_meta:27:link_text', '', ''),
+ MultilingualString::fromTranslations('term_meta:27:link_url', '', ''),
+ ),
+ ),
+ [25, 27],
+ ];
+
+ yield 'number_of_students' => [
+ NumberOfStudents::new('term:25', '>10', ''),
+ [25],
+ ];
+
+ yield 'degree' => [
+ Degree::new(
+ 'term:25',
+ MultilingualString::fromTranslations('term:25:name', '', ''),
+ MultilingualString::fromTranslations('term_meta:25:name', '', ''),
+ null
+ ),
+ [25],
+ ];
+ }
+
+ public function testTermListIdsForPostMeta(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $sut = new IdGenerator();
+ $sut->termIdsList(
+ MultilingualString::fromTranslations('post_meta:25:description', '', '')
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/Repository/WpQueryPaginationAwareCollectionTest.php b/vendor/rrze/fau-studium-common/tests/functional/Repository/WpQueryPaginationAwareCollectionTest.php
new file mode 100644
index 0000000..d60a024
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/Repository/WpQueryPaginationAwareCollectionTest.php
@@ -0,0 +1,79 @@
+init();
+ $query->posts = [1, 2, 3, 4, 5];
+ $query->found_posts = 51;
+ $query->set('paged', 2);
+ $query->set('posts_per_page', 5);
+
+ $sut = new WpQueryPaginationAwareCollection($query);
+ $this->assertSame(2, $sut->currentPage());
+ $this->assertSame(3, $sut->nextPage());
+ $this->assertSame(1, $sut->previousPage());
+ $this->assertSame(11, $sut->maxPages());
+ $this->assertSame(51, $sut->totalItems());
+ }
+
+ public function testFirstPage(): void
+ {
+ $query = new WP_Query();
+ $query->init();
+ $query->posts = [1, 2, 3, 4, 5];
+ $query->found_posts = 51;
+ $query->set('posts_per_page', 5);
+
+ $sut = new WpQueryPaginationAwareCollection($query);
+ $this->assertSame(1, $sut->currentPage());
+ $this->assertSame(2, $sut->nextPage());
+ $this->assertNull($sut->previousPage());
+ $this->assertSame(11, $sut->maxPages());
+ $this->assertSame(51, $sut->totalItems());
+ }
+
+ public function testLastPage(): void
+ {
+ $query = new WP_Query();
+ $query->init();
+ $query->posts = [1, 2, 3, 4, 5];
+ $query->found_posts = 51;
+ $query->set('paged', 11);
+ $query->set('posts_per_page', 5);
+
+ $sut = new WpQueryPaginationAwareCollection($query);
+ $this->assertSame(11, $sut->currentPage());
+ $this->assertNull($sut->nextPage());
+ $this->assertSame(10, $sut->previousPage());
+ $this->assertSame(11, $sut->maxPages());
+ $this->assertSame(51, $sut->totalItems());
+ }
+
+ public function testInvalid(): void
+ {
+ $query = new WP_Query();
+ $query->init();
+ $query->posts = [];
+ $query->found_posts = 51;
+ $query->set('paged', 15);
+ $query->set('posts_per_page', 5);
+
+ $sut = new WpQueryPaginationAwareCollection($query);
+ $this->assertSame(15, $sut->currentPage());
+ $this->assertNull($sut->nextPage());
+ $this->assertSame(11, $sut->previousPage());
+ $this->assertSame(11, $sut->maxPages());
+ $this->assertSame(51, $sut->totalItems());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/Sanitizer/HtmlDegreeProgramSanitizerTest.php b/vendor/rrze/fau-studium-common/tests/functional/Sanitizer/HtmlDegreeProgramSanitizerTest.php
new file mode 100644
index 0000000..8456c67
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/Sanitizer/HtmlDegreeProgramSanitizerTest.php
@@ -0,0 +1,39 @@
+ '');
+ add_shortcode('wrong_shortcode', static fn() => '');
+
+ $sut = new HtmlDegreeProgramSanitizer();
+
+ $this->assertHtmlEqual($output, $sut->sanitizeContentField($input));
+ }
+
+ public function serializedBlocksDataProvider(): iterable
+ {
+ $input = (string) file_get_contents(
+ RESOURCES_DIR . '/fixtures/content_field_html_input.html'
+ );
+ $output = (string) file_get_contents(
+ RESOURCES_DIR . '/fixtures/content_field_html_output.html'
+ );
+
+ yield 'basic_data' => [$input, $output];
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/Sanitizer/SerializedBlocksDegreeProgramSanitizerTest.php b/vendor/rrze/fau-studium-common/tests/functional/Sanitizer/SerializedBlocksDegreeProgramSanitizerTest.php
new file mode 100644
index 0000000..aac8177
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/Sanitizer/SerializedBlocksDegreeProgramSanitizerTest.php
@@ -0,0 +1,35 @@
+assertHtmlEqual($output, $sut->sanitizeContentField($input));
+ }
+
+ public function serializedBlocksDataProvider(): iterable
+ {
+ $input = (string) file_get_contents(
+ RESOURCES_DIR . '/fixtures/content_field_serialized_blocks_input.html'
+ );
+ $output = (string) file_get_contents(
+ RESOURCES_DIR . '/fixtures/content_field_serialized_blocks_output.html'
+ );
+
+ yield 'basic_data' => [$input, $output];
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/BasicRenderingDebugModeTest.php b/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/BasicRenderingDebugModeTest.php
new file mode 100644
index 0000000..41d675c
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/BasicRenderingDebugModeTest.php
@@ -0,0 +1,61 @@
+assertSame(
+ 'Hello World!',
+ $this->sut->render('hello.php', ['hello' => 'Hello World!'])
+ );
+ }
+
+ public function testWithoutExtension(): void
+ {
+ $this->assertSame(
+ 'Hello World!',
+ $this->sut->render('hello', ['hello' => 'Hello World!'])
+ );
+ }
+
+ public function testNonExistingTemplateName(): void
+ {
+ $exceptionHasBeenCaught = false;
+
+ try {
+ $this->sut->render('wrong_name.php');
+ } catch (Throwable $throwable) {
+ $exceptionHasBeenCaught = true;
+ $this->assertInstanceOf(RuntimeException::class, $throwable);
+
+ /** @var Throwable $previous */
+ $previous = $throwable->getPrevious();
+ $this->assertInstanceOf(UnexpectedValueException::class, $previous);
+ $this->assertStringContainsString('wrong_name.php', $previous->getMessage());
+ } finally {
+ $this->assertTrue($exceptionHasBeenCaught);
+ }
+ }
+
+ public function testNonExistingVariables(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessageMatches('/hello\.php/');
+ $this->sut->render('hello.php');
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/BasicRenderingTest.php b/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/BasicRenderingTest.php
new file mode 100644
index 0000000..056fcd1
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/BasicRenderingTest.php
@@ -0,0 +1,18 @@
+assertSame(
+ '',
+ $this->sut->render('wrong', ['hello' => 'Hello World!'])
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/NestedRenderingTest.php b/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/NestedRenderingTest.php
new file mode 100644
index 0000000..50a51c1
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/TemplateRenderer/NestedRenderingTest.php
@@ -0,0 +1,26 @@
+assertHtmlEqual(
+ '',
+ $this->sut->render('list.php', ['list' => $this->prepareList()])
+ );
+ }
+
+ private function prepareList(): ListDto
+ {
+ $listItem = new ListItemDto('https://google.com', 'Google');
+ return new ListDto('Search Engines', $listItem);
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/functional/Validator/JsonSchemaDegreeProgramDataValidatorTest.php b/vendor/rrze/fau-studium-common/tests/functional/Validator/JsonSchemaDegreeProgramDataValidatorTest.php
new file mode 100644
index 0000000..757d2a9
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/functional/Validator/JsonSchemaDegreeProgramDataValidatorTest.php
@@ -0,0 +1,105 @@
+sut = new JsonSchemaDegreeProgramDataValidator(
+ require ROOT_DIR . '/config/schema_draft.php',
+ require ROOT_DIR . '/config/schema_publish.php',
+ new SerializedBlocksDegreeProgramSanitizer(),
+ );
+
+ parent::setUp();
+ }
+
+ /**
+ * @dataProvider fixtureDataProvider
+ */
+ public function testSuccessfulValidation(array $fixtureData): void
+ {
+ $violations = $this->sut->validatePublish($fixtureData);
+ $this->assertCount(0, $violations->getArrayCopy());
+ $violations = $this->sut->validateDraft($fixtureData);
+ $this->assertCount(0, $violations->getArrayCopy());
+ }
+
+ /**
+ * @dataProvider fixtureDataProvider
+ */
+ public function testViolations(array $fixtureData): void
+ {
+ unset($fixtureData['content']);
+ $violations = $this->sut->validateDraft($fixtureData);
+ $this->assertCount(1, $violations->getArrayCopy());
+ $this->assertSame(
+ 'content is a required property of degree_program.',
+ $violations['degree_program']->errorMessage()
+ );
+ }
+
+ /**
+ * @dataProvider invalidDeadlineDataProvider
+ */
+ public function testInvalidApplicationDeadlines(string $deadline): void
+ {
+ $fixtureData = $this->fixtureData();
+ $fixtureData['application_deadline_winter_semester'] = $deadline;
+
+ $violations = $this->sut->validatePublish($fixtureData);
+ $this->assertCount(1, $violations->getArrayCopy());
+ $this->assertStringStartsWith(
+ 'application_deadline_winter_semester does not match pattern',
+ $violations['application_deadline_winter_semester']->errorMessage(),
+ );
+ }
+ /**
+ * @dataProvider validDeadlineDataProvider
+ */
+ public function testValidApplicationDeadlines(string $deadline): void
+ {
+ $fixtureData = $this->fixtureData();
+ $fixtureData['application_deadline_winter_semester'] = $deadline;
+
+ $violations = $this->sut->validatePublish($fixtureData);
+ $this->assertCount(0, $violations->getArrayCopy());
+ }
+
+ public function invalidDeadlineDataProvider(): array
+ {
+ return [
+ ['1'],
+ ['ab.bc.'],
+ ['12.123'],
+ ['12.13.'],
+ ['31.04.'],
+ ['30.04'],
+ ['1.4.'],
+ ];
+ }
+
+ public function validDeadlineDataProvider(): array
+ {
+ return [
+ ['12.12.'],
+ ['01.01.'],
+ ['30.04.'],
+ ['31.08.'],
+ ['20.02.'],
+ [''],
+ ];
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_html_input.html b/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_html_input.html
new file mode 100644
index 0000000..33ba680
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_html_input.html
@@ -0,0 +1,24 @@
+test test
+
+
+
+Heading Level 3
+
+Heading Level 2
+
+
+
+
+ - Ordered list
+
+
+
+ - term
+ - details
+
+
+[fau-video attr="56"]
+
+[wrong_shortcode]
diff --git a/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_html_output.html b/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_html_output.html
new file mode 100644
index 0000000..5a34c18
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_html_output.html
@@ -0,0 +1,22 @@
+test test
+
+
+
+Heading Level 3
+
+Heading Level 2
+
+
+
+
+ - Ordered list
+
+
+
+ - term
+ - details
+
+
+[fau-video attr="56"]
diff --git a/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_serialized_blocks_input.html b/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_serialized_blocks_input.html
new file mode 100644
index 0000000..16e7090
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_serialized_blocks_input.html
@@ -0,0 +1,45 @@
+
+test test
+
+
+
+
+
+
+
+Heading Level 3
+
+
+
+Heading Level 2
+
+
+
+
+
+
+
+
+ - Ordered list
+
+
+
+
+
+ - term
+
+
+
+ - details
+
+
+
+
+[fau-video attr="56"]
+
+
+
+[wrong_shortcode]
+
diff --git a/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_serialized_blocks_output.html b/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_serialized_blocks_output.html
new file mode 100644
index 0000000..24e58b4
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/resources/fixtures/content_field_serialized_blocks_output.html
@@ -0,0 +1,37 @@
+
+test test
+
+
+
+
+
+
+
+Heading Level 3
+
+
+
+
+
+
+
+
+ - Ordered list
+
+
+
+
+
+ - term
+
+
+
+ - details
+
+
+
+
+[fau-video attr="56"]
+
diff --git a/vendor/rrze/fau-studium-common/tests/resources/fixtures/degree_program.json b/vendor/rrze/fau-studium-common/tests/resources/fixtures/degree_program.json
new file mode 100644
index 0000000..0c8ce1a
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/resources/fixtures/degree_program.json
@@ -0,0 +1,668 @@
+{
+ "id": 25,
+ "slug": {
+ "id": "post:25:post_name",
+ "de": "master-of-art-fau-od",
+ "en": "master-of-art-fau-en-ode"
+ },
+ "featured_image": {
+ "id": 9,
+ "url": "https:\/\/fau.localhost\/wp-content\/uploads\/2022\/12\/viber_image_2022-05-27_15-30-39-852.jpg"
+ },
+ "teaser_image": {
+ "id": 14,
+ "url": "https:\/\/fau.localhost\/wp-content\/uploads\/2022\/12\/abstract-1-1528080.jpg"
+ },
+ "title": {
+ "id": "post:25:title",
+ "de": "Master of Art FAU",
+ "en": "Master of Art FAU EN"
+ },
+ "subtitle": {
+ "id": "post_meta:25:subtitle",
+ "de": "Subtitle",
+ "en": "Subtitle EN"
+ },
+ "standard_duration": "6",
+ "fee_required": true,
+ "start": [
+ {
+ "id": "term:21:name",
+ "de": "Summer",
+ "en": "Summer EN"
+ },
+ {
+ "id": "term:22:name",
+ "de": "Winter",
+ "en": "Winter EN"
+ }
+ ],
+ "number_of_students": {
+ "id": "term:9",
+ "name": ">50",
+ "description": "Less<\/p>"
+ },
+ "teaching_language": {
+ "id": "term:10:name",
+ "de": "German Formal",
+ "en": "German Formal EN"
+ },
+ "attributes": [
+ {
+ "id": "term:5:name",
+ "de": "DE",
+ "en": "EN"
+ },
+ {
+ "id": "term:34:name",
+ "de": "New DE",
+ "en": "New EN"
+ }
+ ],
+ "degree": {
+ "id": "term:7",
+ "name": {
+ "id": "term:7:name",
+ "de": "One Degree",
+ "en": "One Degree EN"
+ },
+ "abbreviation": {
+ "id": "term_meta:7:abbreviation",
+ "de": "OD",
+ "en": "ODE"
+ },
+ "parent": {
+ "id": "term:26",
+ "name": {
+ "id": "term:26:name",
+ "de": "Bachelorstudiengänge",
+ "en": "Bachelor Degrees"
+ },
+ "abbreviation": {
+ "id": "term_meta:26:abbreviation",
+ "de": "",
+ "en": ""
+ },
+ "parent": null
+ }
+ },
+ "faculty": [
+ {
+ "id": "term:11",
+ "name": {
+ "id": "term:11:name",
+ "de": "Faculty Math",
+ "en": "Faculty Math EN"
+ },
+ "link_text": {
+ "id": "term_meta:11:link_text",
+ "de": "Link Faculty Math",
+ "en": "Link Faculty Math EN"
+ },
+ "link_url": {
+ "id": "term_meta:11:link_url",
+ "de": "https:\/\/fau.localhost\/faculty-math",
+ "en": "https:\/\/fau.localhost\/faculty-math-en"
+ }
+ }
+ ],
+ "location": [
+ {
+ "id": "term:22:name",
+ "de": "Study location",
+ "en": "Study location EN"
+ }
+ ],
+ "subject_groups": [
+ {
+ "id": "term:23:name",
+ "de": "Subject Bio",
+ "en": "Subject Bio EN"
+ }
+ ],
+ "videos": [
+ "https:\/\/www.youtube.com\/",
+ "https:\/\/vimeo.com\/"
+ ],
+ "meta_description": {
+ "id": "post_meta:25:meta_description",
+ "de": "Meta description.",
+ "en": "Meta description EN."
+ },
+ "content": {
+ "about": {
+ "title": {
+ "id": "option:fau_about",
+ "de": "Worum geht es im Studiengang?",
+ "en": "What is the degree program about?"
+ },
+ "description": {
+ "id": "post_meta:25:about",
+ "de": "It's very interesting.",
+ "en": "It's very interesting EN."
+ }
+ },
+ "structure": {
+ "title": {
+ "id": "option:fau_structure",
+ "de": "",
+ "en": ""
+ },
+ "description": {
+ "id": "post_meta:25:structure",
+ "de": "Structure description.",
+ "en": "Structure description EN."
+ }
+ },
+ "specializations": {
+ "title": {
+ "id": "option:fau_specializations",
+ "de": "Spec title",
+ "en": "Spec title EN"
+ },
+ "description": {
+ "id": "post_meta:25:specializations",
+ "de": "Specializations description.",
+ "en": "Specializations description EN."
+ }
+ },
+ "qualities_and_skills": {
+ "title": {
+ "id": "option:fau_qualities_and_skills",
+ "de": "Qualities ans skills title.",
+ "en": "Qualities ans skills title EN."
+ },
+ "description": {
+ "id": "post_meta:25:qualities_and_skills",
+ "de": "Qualities ans skills.",
+ "en": "Qualities ans skills EN."
+ }
+ },
+ "why_should_study": {
+ "title": {
+ "id": "option:fau_why_should_study",
+ "de": "Gute Gr\u00fcnde f\u00fcr ein Studium an der FAU1232",
+ "en": "Why should I study at FAU?"
+ },
+ "description": {
+ "id": "post_meta:25:why_should_study",
+ "de": "Why should study description.",
+ "en": "Why should study description EN."
+ }
+ },
+ "career_prospects": {
+ "title": {
+ "id": "option:fau_career_prospects",
+ "de": "Career title",
+ "en": "Career title EN."
+ },
+ "description": {
+ "id": "post_meta:25:career_prospects",
+ "de": "Career description.",
+ "en": "Career description EN."
+ }
+ },
+ "special_features": {
+ "title": {
+ "id": "option:fau_special_features",
+ "de": "Special title",
+ "en": "Special title EN"
+ },
+ "description": {
+ "id": "post_meta:25:special_features",
+ "de": "Special description",
+ "en": "Special description EN"
+ }
+ },
+ "testimonials": {
+ "title": {
+ "id": "option:fau_testimonials",
+ "de": "Erfahrungsberichte",
+ "en": "Testimonials"
+ },
+ "description": {
+ "id": "post_meta:25:testimonials",
+ "de": "Testimonial description",
+ "en": "Testimonial description EN"
+ }
+ }
+ },
+ "admission_requirements": {
+ "bachelor_or_teaching_degree": {
+ "id": "term:13",
+ "name": {
+ "id": "term:13:name",
+ "de": "Admission Bachelor",
+ "en": "Admission Bachelor EN"
+ },
+ "link_text": {
+ "id": "term_meta:13:link_text",
+ "de": "Link Admission Bachelor",
+ "en": "Link Admission Bachelor"
+ },
+ "link_url": {
+ "id": "term_meta:13:link_url",
+ "de": "https:\/\/fau.localhost\/admission-bachelor",
+ "en": "https:\/\/fau.localhost\/admission-bachelor-en"
+ },
+ "parent": {
+ "id": "term:123",
+ "name": {
+ "id": "term:123:name",
+ "de": "Frei",
+ "en": "Free"
+ },
+ "link_text": {
+ "id": "term_meta:123:link_text",
+ "de": "",
+ "en": ""
+ },
+ "link_url": {
+ "id": "term_meta:123:link_url",
+ "de": "",
+ "en": ""
+ },
+ "parent": null,
+ "slug": "frei"
+ },
+ "slug": "admission_bachelor"
+ },
+ "teaching_degree_higher_semester": {
+ "id": "term:24",
+ "name": {
+ "id": "term:24:name",
+ "de": "Admission requirement for entering a Bachelor's\/teaching degree at a higher semester",
+ "en": "Admission requirement for entering a Bachelor's\/teaching degree at a higher semester EN"
+ },
+ "link_text": {
+ "id": "term_meta:24:link_text",
+ "de": "Link to Admission requirement for entering a Bachelor's\/teaching degree at a higher semester",
+ "en": "Link to Admission requirement for entering a Bachelor's\/teaching degree at a higher semester EN"
+ },
+ "link_url": {
+ "id": "term_meta:24:link_url",
+ "de": "https:\/\/fau.localhost\/higher-semester",
+ "en": "https:\/\/fau.localhost\/higher-semester-en"
+ },
+ "parent": {
+ "id": "term:124",
+ "name": {
+ "id": "term:124:name",
+ "de": "Frei",
+ "en": "Free"
+ },
+ "link_text": {
+ "id": "term_meta:124:link_text",
+ "de": "",
+ "en": ""
+ },
+ "link_url": {
+ "id": "term_meta:124:link_url",
+ "de": "",
+ "en": ""
+ },
+ "parent": null,
+ "slug": "frei"
+ },
+ "slug": "admission_higher_semester"
+ },
+ "master": {
+ "id": "term:19",
+ "name": {
+ "id": "term:19:name",
+ "de": "Admission requirement Master\u2019s degree",
+ "en": "Admission requirement Master\u2019s degree EN"
+ },
+ "link_text": {
+ "id": "term_meta:19:link_text",
+ "de": "Link to Admission requirement Master\u2019s degree",
+ "en": "Link to Admission requirement Master\u2019s degree EN"
+ },
+ "link_url": {
+ "id": "term_meta:19:link_url",
+ "de": "https:\/\/fau.localhost\/admission-master",
+ "en": "https:\/\/fau.localhost\/admission-master-en"
+ },
+ "parent": {
+ "id": "term:125",
+ "name": {
+ "id": "term:125:name",
+ "de": "Nich Frei",
+ "en": "Not Free"
+ },
+ "link_text": {
+ "id": "term_meta:125:link_text",
+ "de": "",
+ "en": ""
+ },
+ "link_url": {
+ "id": "term_meta:125:link_url",
+ "de": "",
+ "en": ""
+ },
+ "parent": null,
+ "slug": "nich_frei"
+ },
+ "slug": "admission_master"
+ }
+ },
+ "content_related_master_requirements": {
+ "id": "post_meta:25:content_related_master_requirements",
+ "de": "Master requirements.",
+ "en": "Master requirements EN."
+ },
+ "application_deadline_winter_semester": "01.12.",
+ "application_deadline_summer_semester": "01.07.",
+ "details_and_notes": {
+ "id": "post_meta:25:details_and_notes",
+ "de": "Notes.",
+ "en": "Notes EN."
+ },
+ "language_skills": {
+ "id": "post_meta:25:language_skills",
+ "de": "C1",
+ "en": "C1"
+ },
+ "language_skills_humanities_faculty": "Excellent",
+ "german_language_skills_for_international_students": {
+ "id": "term:16",
+ "name": {
+ "id": "term:16:name",
+ "de": "German language skills for international students",
+ "en": "German language skills for international students EN"
+ },
+ "link_text": {
+ "id": "term_meta:16:link_text",
+ "de": "Link to german language skills for international students",
+ "en": "Link to german language skills for international students EN"
+ },
+ "link_url": {
+ "id": "term_meta:16:link_url",
+ "de": "https:\/\/fau.localhost\/german-language-skills-international-students",
+ "en": "https:\/\/fau.localhost\/german-language-skills-international-students-en"
+ }
+ },
+ "start_of_semester": {
+ "id": "option:fau_start_of_semester",
+ "name": {
+ "id": "option:fau_start_of_semester:name",
+ "de": "Early start",
+ "en": "Early start EN"
+ },
+ "link_text": {
+ "id": "option:fau_start_of_semester:link_text",
+ "de": "Link to Start of Semester",
+ "en": "Link to Start of Semester EN"
+ },
+ "link_url": {
+ "id": "option:fau_start_of_semester:link_url",
+ "de": "https:\/\/fau.localhost\/start-of-semester",
+ "en": "https:\/\/fau.localhost\/start-of-semester-en"
+ }
+ },
+ "semester_dates": {
+ "id": "option:fau_semester_dates",
+ "name": {
+ "id": "option:fau_semester_dates:name",
+ "de": "1/09-23/12",
+ "en": "1/09-23/12"
+ },
+ "link_text": {
+ "id": "option:fau_semester_dates:link_text",
+ "de": "Link text Semester dates",
+ "en": "Link text Semester dates EN"
+ },
+ "link_url": {
+ "id": "option:fau_semester_dates:link_url",
+ "de": "https:\/\/fau.localhost\/semester-dates",
+ "en": "https:\/\/fau.localhost\/semester-dates-en"
+ }
+ },
+ "examinations_office": {
+ "id": "term:15",
+ "name": {
+ "id": "term:15:name",
+ "de": "Examinations Office",
+ "en": "Examinations Office EN"
+ },
+ "link_text": {
+ "id": "term_meta:15:link_text",
+ "de": "Link Examinations Office",
+ "en": "Link Examinations Office EN"
+ },
+ "link_url": {
+ "id": "term_meta:15:link_url",
+ "de": "https:\/\/fau.localhost\/examinations-office",
+ "en": "https:\/\/fau.localhost\/examinations-office-en"
+ }
+ },
+ "examination_regulations": "https://fau.localhost/examinations-regulations",
+ "module_handbook": "Module handbook value",
+ "url": {
+ "id": "post_meta:25:url",
+ "de": "https:\/\/fau.localhost\/degree",
+ "en": "https:\/\/fau.localhost\/degree-program"
+ },
+ "department": {
+ "id": "post_meta:25:department",
+ "de": "https:\/\/fau.localhost\/science",
+ "en": "https:\/\/fau.localhost\/science-en"
+ },
+ "student_advice": {
+ "id": "option:fau_student_advice",
+ "name": {
+ "id": "option:fau_student_advice:name",
+ "de": "Student Advice and Career Service",
+ "en": "Student Advice and Career Service EN"
+ },
+ "link_text": {
+ "id": "option:fau_student_advice:link_text",
+ "de": "Link Student Advice and Career Service",
+ "en": "Link Student Advice and Career Service EN"
+ },
+ "link_url": {
+ "id": "option:fau_student_advice:link_url",
+ "de": "https:\/\/fau.localhost\/career-service",
+ "en": "https:\/\/fau.localhost\/career-service-en"
+ }
+ },
+ "subject_specific_advice": {
+ "id": "term:6",
+ "name": {
+ "id": "term:6:name",
+ "de": "Advice",
+ "en": "Advice EN"
+ },
+ "link_text": {
+ "id": "term_meta:6:link_text",
+ "de": "Link to Advice",
+ "en": "Link to Advice EN"
+ },
+ "link_url": {
+ "id": "term_meta:6:link_url",
+ "de": "https:\/\/fau.localhost\/advice",
+ "en": "https:\/\/fau.localhost\/advice-en"
+ }
+ },
+ "service_centers": {
+ "id": "option:fau_service_centers",
+ "name": {
+ "id": "option:fau_service_centers:name",
+ "de": "Counseling and Service Centers at FAU",
+ "en": "Counseling and Service Centers at FAU EN"
+ },
+ "link_text": {
+ "id": "option:fau_service_centers:link_text",
+ "de": "Link Counseling and Service Centers at FAU",
+ "en": "Link Counseling and Service Centers at FAU EN"
+ },
+ "link_url": {
+ "id": "option:fau_service_centers:link_url",
+ "de": "https:\/\/fau.localhost\/counseling",
+ "en": "https:\/\/fau.localhost\/counseling-en"
+ }
+ },
+ "info_brochure": "Info Brochure 2023",
+ "semester_fee": {
+ "id": "option:fau_semester_fee",
+ "name": {
+ "id": "option:fau_semester_fee:name",
+ "de": "Semester fee",
+ "en": "Semester fee EN"
+ },
+ "link_text": {
+ "id": "option:fau_semester_fee:link_text",
+ "de": "Link Semester fee",
+ "en": "Link Semester fee EN"
+ },
+ "link_url": {
+ "id": "option:fau_semester_fee:link_url",
+ "de": "https:\/\/fau.localhost\/semester-fee",
+ "en": "https:\/\/fau.localhost\/semester-fee-en"
+ }
+ },
+ "degree_program_fees": {
+ "id": "post_meta:25:degree_program_fees",
+ "de": "1000 EUR",
+ "en": "EUR 1000"
+ },
+ "abroad_opportunities": {
+ "id": "option:fau_abroad_opportunities",
+ "name": {
+ "id": "option:fau_abroad_opportunities:name",
+ "de": "Opportunities for spending time abroad",
+ "en": "Opportunities for spending time abroad EN"
+ },
+ "link_text": {
+ "id": "option:fau_abroad_opportunities:link_text",
+ "de": "Link Opportunities for spending time abroad",
+ "en": "Link Opportunities for spending time abroad EN"
+ },
+ "link_url": {
+ "id": "option:fau_abroad_opportunities:link_url",
+ "de": "https:\/\/fau.localhost\/abroad",
+ "en": "https:\/\/fau.localhost\/abroad-en"
+ }
+ },
+ "keywords": [
+ {
+ "id": "term:17:name",
+ "de": "Keyword 1",
+ "en": "Keyword 1 EN"
+ },
+ {
+ "id": "term:18:name",
+ "de": "Keyword 2",
+ "en": "Keyword 2 EN"
+ }
+ ],
+ "area_of_study": [
+ {
+ "id": "term:3",
+ "name": {
+ "id": "term:3:name",
+ "de": "Biology",
+ "en": "Biology EN"
+ },
+ "link_text": {
+ "id": "term_meta:3:link_text",
+ "de": "Link Biology",
+ "en": "Link Biology"
+ },
+ "link_url": {
+ "id": "term_meta:3:link_url",
+ "de": "https:\/\/fau.localhost\/biology",
+ "en": "https:\/\/fau.localhost\/biology-en"
+ }
+ },
+ {
+ "id": "term:38",
+ "name": {
+ "id": "term:38:name",
+ "de": "Math",
+ "en": "Math EN"
+ },
+ "link_text": {
+ "id": "term_meta:38:link_text",
+ "de": "Link Math",
+ "en": "Link Math EN"
+ },
+ "link_url": {
+ "id": "term_meta:38:link_url",
+ "de": "https:\/\/fau.localhost\/biology-math",
+ "en": "https:\/\/fau.localhost\/biology-math-en"
+ }
+ }
+ ],
+ "combinations": [
+ 26,
+ 28
+ ],
+ "limited_combinations": [
+ 26
+ ],
+ "notes_for_international_applicants": {
+ "id": "option:fau_notes_for_international_applicants",
+ "name": {
+ "id": "option:fau_notes_for_international_applicants:name",
+ "de": "Notes for international applicants",
+ "en": "Notes for international applicants EN"
+ },
+ "link_text": {
+ "id": "option:fau_notes_for_international_applicants:link_text",
+ "de": "Link to notes for international applicants",
+ "en": "Link to notes for international applicants EN"
+ },
+ "link_url": {
+ "id": "option:fau_notes_for_international_applicants:link_url",
+ "de": "https:\/\/fau.localhost\/notes-for-intl-applicants",
+ "en": "https:\/\/fau.localhost\/notes-for-intl-applicants-en"
+ }
+ },
+ "student_initiatives": {
+ "id": "option:student_initiatives",
+ "name": {
+ "id": "option:student_initiatives:name",
+ "de": "Students' Union\/Student Initiatives",
+ "en": "Students' Union/Student Initiatives EN"
+ },
+ "link_text": {
+ "id": "option:student_initiatives:link_text",
+ "de": "Link Students' Union\/Student Initiatives",
+ "en": "Link Students' Union\/Student Initiatives EN"
+ },
+ "link_url": {
+ "id": "option:student_initiatives:link_url",
+ "de": "https:\/\/fau.localhost\/fsi",
+ "en": "https:\/\/fau.localhost\/fsi-en"
+ }
+ },
+ "apply_now_link": {
+ "id": "term:39",
+ "name": {
+ "id": "term:39:name",
+ "de": "Campo",
+ "en": "Campo"
+ },
+ "link_text": {
+ "id": "term_meta:39:link_text",
+ "de": "Apply now",
+ "en": "Apply now"
+ },
+ "link_url": {
+ "id": "term_meta:39:link_url",
+ "de": "https://campo.fau.de",
+ "en": "https://campo.fau.de"
+ }
+ },
+ "entry_text": {
+ "id": "post_meta:25:entry_text",
+ "de": "Einstiegtext (werbend)",
+ "en": "Entry text (promotional)"
+ },
+ "campo_keys": {
+ "degree": "185",
+ "area_of_study": "85",
+ "location": "E"
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/resources/templates/hello.php b/vendor/rrze/fau-studium-common/tests/resources/templates/hello.php
new file mode 100644
index 0000000..35a44d9
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/resources/templates/hello.php
@@ -0,0 +1,11 @@
+ $hello] = $data;
+
+echo $hello;
diff --git a/vendor/rrze/fau-studium-common/tests/resources/templates/list.php b/vendor/rrze/fau-studium-common/tests/resources/templates/list.php
new file mode 100644
index 0000000..ab04272
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/resources/templates/list.php
@@ -0,0 +1,21 @@
+ $list] = $data; ?>
+
+
+ listItems() as $item) {
+ echo $renderer->render('partials/item.php', ['item' => $item]);
+ } ?>
+
diff --git a/vendor/rrze/fau-studium-common/tests/resources/templates/partials/item.php b/vendor/rrze/fau-studium-common/tests/resources/templates/partials/item.php
new file mode 100644
index 0000000..c867066
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/resources/templates/partials/item.php
@@ -0,0 +1,22 @@
+ $item] = $data; ?>
+
+render(
+ 'partials/link.php',
+ ['nested_array' => ['url' => $item->url(), 'title' => $item->title()]]
+) ?>
+
+
+ ['url' => $url, 'title' => $title]] = $data; ?>
+
+= $title ?>
diff --git a/vendor/rrze/fau-studium-common/tests/src/AsserHtmlTrait.php b/vendor/rrze/fau-studium-common/tests/src/AsserHtmlTrait.php
new file mode 100644
index 0000000..a283b0f
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/AsserHtmlTrait.php
@@ -0,0 +1,21 @@
+assertSame(
+ $this->normalizeSpaces($expected),
+ $this->normalizeSpaces($actual)
+ );
+ }
+
+ private function normalizeSpaces(string $html): string
+ {
+ return (string) preg_replace('/[\s]{2,}|\n/', '', $html);
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/Dto/ListDto.php b/vendor/rrze/fau-studium-common/tests/src/Dto/ListDto.php
new file mode 100644
index 0000000..885d5e1
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/Dto/ListDto.php
@@ -0,0 +1,31 @@
+title = $title;
+ $this->listItems = $listItems;
+ }
+
+ public function title(): string
+ {
+ return $this->title;
+ }
+
+ public function listItems(): array
+ {
+ return $this->listItems;
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/Dto/ListItemDto.php b/vendor/rrze/fau-studium-common/tests/src/Dto/ListItemDto.php
new file mode 100644
index 0000000..0e14134
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/Dto/ListItemDto.php
@@ -0,0 +1,28 @@
+url = $url;
+ $this->title = $title;
+ }
+
+ public function url(): string
+ {
+ return $this->url;
+ }
+
+ public function title(): string
+ {
+ return $this->title;
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/FixtureDegreeProgramDataProviderTrait.php b/vendor/rrze/fau-studium-common/tests/src/FixtureDegreeProgramDataProviderTrait.php
new file mode 100644
index 0000000..c5cb2d5
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/FixtureDegreeProgramDataProviderTrait.php
@@ -0,0 +1,117 @@
+ [$this->fixtureData()];
+ }
+
+ public function createEmptyDegreeProgram(int $id): DegreeProgram
+ {
+ return new DegreeProgram(
+ id: DegreeProgramId::fromInt($id),
+ slug: MultilingualString::empty(),
+ featuredImage: Image::empty(),
+ teaserImage: Image::empty(),
+ title: MultilingualString::empty(),
+ subtitle: MultilingualString::empty(),
+ standardDuration: '',
+ start: MultilingualList::new(),
+ numberOfStudents: NumberOfStudents::empty(),
+ teachingLanguage: MultilingualString::empty(),
+ attributes: MultilingualList::new(),
+ degree: Degree::empty(),
+ faculty: MultilingualLinks::new(),
+ location: MultilingualList::new(),
+ subjectGroups: MultilingualList::new(),
+ videos: ArrayOfStrings::new(),
+ metaDescription: MultilingualString::empty(),
+ keywords: MultilingualList::new(),
+ areaOfStudy: MultilingualLinks::new(),
+ entryText: MultilingualString::empty(),
+ content: Content::new(
+ about: ContentItem::new(MultilingualString::empty(), MultilingualString::empty()),
+ structure: ContentItem::new(MultilingualString::empty(), MultilingualString::empty()),
+ specializations: ContentItem::new(MultilingualString::empty(), MultilingualString::empty()),
+ qualitiesAndSkills: ContentItem::new(MultilingualString::empty(), MultilingualString::empty()),
+ whyShouldStudy: ContentItem::new(MultilingualString::empty(), MultilingualString::empty()),
+ careerProspects: ContentItem::new(MultilingualString::empty(), MultilingualString::empty()),
+ specialFeatures: ContentItem::new(MultilingualString::empty(), MultilingualString::empty()),
+ testimonials: ContentItem::new(MultilingualString::empty(), MultilingualString::empty()),
+ ),
+ admissionRequirements: AdmissionRequirements::new(
+ bachelorOrTeachingDegree: AdmissionRequirement::empty(),
+ teachingDegreeHigherSemester: AdmissionRequirement::empty(),
+ master: AdmissionRequirement::empty(),
+ ),
+ contentRelatedMasterRequirements: MultilingualString::empty(),
+ applicationDeadlineWinterSemester: '',
+ applicationDeadlineSummerSemester: '',
+ detailsAndNotes: MultilingualString::empty(),
+ languageSkills: MultilingualString::empty(),
+ languageSkillsHumanitiesFaculty: '',
+ germanLanguageSkillsForInternationalStudents: MultilingualLink::empty(),
+ startOfSemester: MultilingualLink::empty(),
+ semesterDates: MultilingualLink::empty(),
+ examinationsOffice: MultilingualLink::empty(),
+ examinationRegulations: '',
+ moduleHandbook: '',
+ url: MultilingualString::empty(),
+ department: MultilingualString::empty(),
+ studentAdvice: MultilingualLink::empty(),
+ subjectSpecificAdvice: MultilingualLink::empty(),
+ serviceCenters: MultilingualLink::empty(),
+ infoBrochure: '',
+ semesterFee: MultilingualLink::empty(),
+ feeRequired: false,
+ degreeProgramFees: MultilingualString::empty(),
+ abroadOpportunities: MultilingualLink::empty(),
+ notesForInternationalApplicants: MultilingualLink::empty(),
+ studentInitiatives: MultilingualLink::empty(),
+ applyNowLink: MultilingualLink::empty(),
+ combinations: DegreeProgramIds::new(),
+ limitedCombinations: DegreeProgramIds::new(),
+ campoKeys: CampoKeys::empty(),
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/RendererTestCase.php b/vendor/rrze/fau-studium-common/tests/src/RendererTestCase.php
new file mode 100644
index 0000000..b92043b
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/RendererTestCase.php
@@ -0,0 +1,25 @@
+sut = TemplateRenderer::new(
+ DirectoryLocator::new(TEMPLATES_DIR),
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/Repository/StubDegreeProgramRepository.php b/vendor/rrze/fau-studium-common/tests/src/Repository/StubDegreeProgramRepository.php
new file mode 100644
index 0000000..cc82796
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/Repository/StubDegreeProgramRepository.php
@@ -0,0 +1,161 @@
+
+ */
+ private array $store = [];
+
+ public function getById(DegreeProgramId $degreeProgramId): DegreeProgram
+ {
+ return $this->store[$degreeProgramId->asInt()]
+ ?? throw new RuntimeException(
+ 'Could not find degree program with id ' . (string) $degreeProgramId->asInt()
+ );
+ }
+
+ public function save(DegreeProgram $degreeProgram): void
+ {
+ $raw = DegreeProgramViewRaw::fromDegreeProgram($degreeProgram);
+ $this->store[$raw->id()->asInt()] = $degreeProgram;
+ }
+
+ public function findRaw(DegreeProgramId $degreeProgramId): ?DegreeProgramViewRaw
+ {
+ return isset($this->store[$degreeProgramId->asInt()])
+ ? DegreeProgramViewRaw::fromDegreeProgram($this->store[$degreeProgramId->asInt()])
+ : null;
+ }
+
+ public function findTranslated(DegreeProgramId $degreeProgramId, string $languageCode): ?DegreeProgramViewTranslated
+ {
+ $raw = $this->findRaw($degreeProgramId);
+ if (!$raw instanceof DegreeProgramViewRaw) {
+ return null;
+ }
+
+ $main = $this->translateDegreeProgram($raw, $languageCode);
+ foreach (MultilingualString::LANGUAGES as $code => $name) {
+ if ($code === $languageCode) {
+ continue;
+ }
+
+ $main = $main->withTranslation(
+ $this->translateDegreeProgram($raw, $code),
+ $code
+ );
+ }
+
+ return $main;
+ }
+
+ private function translateDegreeProgram(
+ DegreeProgramViewRaw $raw,
+ string $languageCode
+ ): DegreeProgramViewTranslated {
+
+ return new DegreeProgramViewTranslated(
+ id: $raw->id(),
+ link: '',
+ slug: '',
+ lang: $languageCode,
+ featuredImage: ImageView::new(
+ $raw->featuredImage()->id(),
+ $raw->featuredImage()->url(),
+ '',
+ ),
+ teaserImage: ImageView::new(
+ $raw->teaserImage()->id(),
+ $raw->teaserImage()->url(),
+ '',
+ ),
+ title: $raw->title()->asString($languageCode),
+ subtitle: $raw->subtitle()->asString($languageCode),
+ standardDuration: $raw->standardDuration(),
+ feeRequired: $raw->isFeeRequired(),
+ start: $raw->start()->asArrayOfStrings($languageCode),
+ numberOfStudents: $raw->numberOfStudents()->asString(),
+ teachingLanguage: $raw->teachingLanguage()->asString($languageCode),
+ attributes: $raw->attributes()->asArrayOfStrings($languageCode),
+ degree: DegreeTranslated::fromDegree($raw->degree(), $languageCode),
+ faculty: Links::fromMultilingualLinks($raw->faculty(), $languageCode),
+ location: $raw->location()->asArrayOfStrings($languageCode),
+ subjectGroups: $raw->subjectGroups()->asArrayOfStrings($languageCode),
+ videos: $raw->videos(),
+ metaDescription: $raw->metaDescription()->asString($languageCode),
+ content: ContentTranslated::fromContent($raw->content(), $languageCode),
+ admissionRequirements: AdmissionRequirementsTranslated::fromAdmissionRequirements(
+ $raw->admissionRequirements(),
+ $languageCode
+ ),
+ contentRelatedMasterRequirements: $raw->contentRelatedMasterRequirements()->asString($languageCode),
+ applicationDeadlineWinterSemester: $raw->applicationDeadlineWinterSemester(),
+ applicationDeadlineSummerSemester: $raw->applicationDeadlineSummerSemester(),
+ detailsAndNotes: $raw->detailsAndNotes()->asString($languageCode),
+ languageSkills: $raw->languageSkills()->asString($languageCode),
+ languageSkillsHumanitiesFaculty: $raw->languageSkillsHumanitiesFaculty(),
+ germanLanguageSkillsForInternationalStudents: Link::fromMultilingualLink(
+ $raw->germanLanguageSkillsForInternationalStudents(),
+ $languageCode
+ ),
+ startOfSemester: Link::fromMultilingualLink($raw->startOfSemester(), $languageCode),
+ semesterDates: Link::fromMultilingualLink($raw->semesterDates(), $languageCode),
+ examinationsOffice: Link::fromMultilingualLink($raw->examinationsOffice(), $languageCode),
+ examinationRegulations: $raw->examinationRegulations(),
+ moduleHandbook: $raw->moduleHandbook(),
+ url: $raw->url()->asString($languageCode),
+ department: $raw->department()->asString($languageCode),
+ studentAdvice: Link::fromMultilingualLink($raw->studentAdvice(), $languageCode),
+ subjectSpecificAdvice: Link::fromMultilingualLink($raw->subjectSpecificAdvice(), $languageCode),
+ serviceCenters: Link::fromMultilingualLink($raw->serviceCenters(), $languageCode),
+ infoBrochure: $raw->infoBrochure(),
+ semesterFee: Link::fromMultilingualLink($raw->semesterFee(), $languageCode),
+ degreeProgramFees: $raw->degreeProgramFees()->asString($languageCode),
+ abroadOpportunities: Link::fromMultilingualLink($raw->abroadOpportunities(), $languageCode),
+ keywords: $raw->keywords()->asArrayOfStrings($languageCode),
+ areaOfStudy: Links::fromMultilingualLinks($raw->areaOfStudy(), $languageCode),
+ combinations: RelatedDegreePrograms::new(),
+ limitedCombinations: RelatedDegreePrograms::new(),
+ notesForInternationalApplicants: Link::fromMultilingualLink($raw->notesForInternationalApplicants(), $languageCode),
+ applyNowLink: Link::fromMultilingualLink($raw->applyNowLink(), $languageCode),
+ entryText: $raw->entryText()->asString($languageCode),
+ campoKeys: $raw->campoKeys()->asArray(),
+ );
+ }
+
+ public function findRawCollection(CollectionCriteria $criteria): PaginationAwareCollection
+ {
+ return new StubPaginationAwareCollection();
+ }
+
+ public function findTranslatedCollection(CollectionCriteria $criteria, string $languageCode): PaginationAwareCollection
+ {
+ return new StubPaginationAwareCollection();
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/Repository/StubPaginationAwareCollection.php b/vendor/rrze/fau-studium-common/tests/src/Repository/StubPaginationAwareCollection.php
new file mode 100644
index 0000000..8b4b16f
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/Repository/StubPaginationAwareCollection.php
@@ -0,0 +1,50 @@
+currentPage;
+ }
+
+ public function nextPage(): ?int
+ {
+ return $this->nextPage;
+ }
+
+ public function previousPage(): ?int
+ {
+ return $this->previousPage;
+ }
+
+ public function maxPages(): int
+ {
+ return $this->maxPages;
+ }
+
+ public function totalItems(): int
+ {
+ return $this->totalItems;
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/Sanitizer/StubSanitizer.php b/vendor/rrze/fau-studium-common/tests/src/Sanitizer/StubSanitizer.php
new file mode 100644
index 0000000..cbd76a4
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/Sanitizer/StubSanitizer.php
@@ -0,0 +1,20 @@
+prefix . $content;
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/UnitTestCase.php b/vendor/rrze/fau-studium-common/tests/src/UnitTestCase.php
new file mode 100644
index 0000000..0a7d3da
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/UnitTestCase.php
@@ -0,0 +1,27 @@
+result;
+ }
+
+ public function validateDraft(array $data): Violations
+ {
+ return Violations::new();
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpDbLessTestCase.php b/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpDbLessTestCase.php
new file mode 100644
index 0000000..81deae2
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpDbLessTestCase.php
@@ -0,0 +1,138 @@
+wpHook = new WpHook();
+ $this->wpOption = new WpOption([
+ 'siteurl' => 'https://wp-db-less',
+ ]);
+
+ if (!defined('ABSPATH')) {
+ define('ABSPATH', (string) getcwd() . '/vendor/johnpbloch/wordpress-core/');
+ }
+
+ require_once ABSPATH . 'wp-includes/class-wpdb.php';
+ $GLOBALS['wpdb'] = new WpDbStub();
+
+ $this->wpHook->addHook(
+ 'alloptions',
+ [$this->wpOption, 'options']
+ );
+ }
+
+ protected function tearDown(): void
+ {
+ unset($GLOBALS['wpdb'], $GLOBALS['wp_filter']);
+ }
+
+ public function run(TestResult $result = null): TestResult
+ {
+ $this->runTestInSeparateProcess = true;
+
+ return parent::run($result);
+ }
+
+ protected function runTest(): void
+ {
+ if ($this->throwable) {
+ parent::runTest();
+ return;
+ }
+
+ $hook = $this->detectHook() ?? 'muplugins_loaded';
+
+ // phpcs:disable Inpsyde.CodeQuality.StaticClosure.PossiblyStaticClosure
+ $this->runWithThrowableCatching($hook, function () {
+ $this->setUpAfterWordPressLoaded();
+ parent::runTest();
+ });
+
+ $this->loadWordPress();
+ }
+
+ private function loadWordPress(): void
+ {
+ // phpcs:disable Inpsyde.CodeQuality.VariablesName.SnakeCaseVar
+ $table_prefix = 'wp_db_less_';
+ require_once ABSPATH . 'wp-settings.php';
+ }
+
+ /**
+ * Exception throwing inside WordPress hook
+ * is not cached by \PHPUnit\Framework\TestCase::runTest
+ * so we provide a workaround to catch it and rethrow
+ * with fake assertThrowable test method.
+ */
+ private function runWithThrowableCatching(
+ string $hook,
+ callable $callback,
+ ): void {
+
+ $exceptionAwareCallback = function () use ($callback): void {
+ try {
+ $callback();
+ } catch (Throwable $throwable) {
+ $this->throwable = $throwable;
+ /** @psalm-suppress InternalMethod */
+ $this->setName('assertThrowable');
+ $this->runTest();
+ }
+ };
+
+ $this->wpHook->addHook($hook, $exceptionAwareCallback, 0);
+ }
+
+ /**
+ * @internal
+ */
+ protected function assertThrowable(): void
+ {
+ if ($this->throwable instanceof Throwable) {
+ throw $this->throwable;
+ }
+ }
+
+ /**
+ * @psalm-suppress InternalMethod
+ * @psalm-suppress InternalClass
+ */
+ private function detectHook(): ?string
+ {
+ $annotations = TestUtil::parseTestMethodAnnotations(
+ static::class,
+ $this->getName()
+ );
+
+ if (!isset($annotations['method']['wp-hook'][0])) {
+ return null;
+ }
+
+ return (string) $annotations['method']['wp-hook'][0];
+ }
+
+ /**
+ * phpcs:disable Inpsyde.CodeQuality.NoAccessors.NoSetter
+ */
+ public function setUpAfterWordPressLoaded(): void
+ {
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpDbStub.php b/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpDbStub.php
new file mode 100644
index 0000000..c50b0b0
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpDbStub.php
@@ -0,0 +1,25 @@
+ PHP_INT_MAX,
+ 'function' => $callback,
+ ];
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpOption.php b/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpOption.php
new file mode 100644
index 0000000..5f646e4
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/src/WpDbLess/WpOption.php
@@ -0,0 +1,22 @@
+options[$optionName] = $optionValue;
+ }
+
+ public function options(): array
+ {
+ return $this->options;
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Application/Repository/CollectionCriteriaTest.php b/vendor/rrze/fau-studium-common/tests/unit/Application/Repository/CollectionCriteriaTest.php
new file mode 100644
index 0000000..99392ca
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Application/Repository/CollectionCriteriaTest.php
@@ -0,0 +1,49 @@
+assertSame(1, $sut->page());
+ $this->assertSame(10, $sut->perPage());
+
+ $sut = $sut->withoutPagination();
+ $this->assertSame(1, $sut->page());
+ $this->assertSame(-1, $sut->perPage());
+
+ $sut = $sut->withPage(2)->withPerPage(20);
+ $this->assertSame(2, $sut->page());
+ $this->assertSame(20, $sut->perPage());
+
+ $sut = $sut->toNextPage();
+ $this->assertSame(3, $sut->page());
+ $this->assertSame(20, $sut->perPage());
+ }
+
+ public function testLanguageCode(): void
+ {
+ $sut = CollectionCriteria::new()->withLanguage('en');
+ $this->assertSame('en', $sut->languageCode());
+ }
+
+ public function testOrdering(): void
+ {
+ $sut = CollectionCriteria::new();
+ $this->assertSame([], $sut->args()['order_by']);
+
+ $sut = $sut->withOrderBy(['title' => 'asc']);
+ $this->assertSame(['title' => 'asc'], $sut->args()['order_by']);
+
+ $sut = $sut->withOrderBy(['unknown' => null, 'title' => 'desc']);
+ $this->assertSame(['title' => 'desc'], $sut->args()['order_by']);
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Content/PostTypeTest.php b/vendor/rrze/fau-studium-common/tests/unit/Content/PostTypeTest.php
new file mode 100644
index 0000000..116a3eb
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Content/PostTypeTest.php
@@ -0,0 +1,62 @@
+args();
+ unset($publicArgs['labels']);
+ $this->assertSame(
+ [
+ 'label' => 'Degree Programs',
+ 'hierarchical' => false,
+ 'supports' => [
+ 'editor',
+ 'author',
+ ],
+ 'public' => true,
+ 'show_in_rest' => true,
+ 'rest_base' => 'degree-program',
+ 'menu_icon' => 'dashicons-welcome-learn-more',
+ ],
+ $publicArgs
+ );
+
+ $hiddenArgs = DegreeProgramPostType::hidden()->args();
+ unset($hiddenArgs['labels']);
+ $this->assertSame(
+ [
+ 'label' => 'Degree Programs',
+ 'hierarchical' => false,
+ 'supports' => [
+ 'editor',
+ 'author',
+ ],
+ 'public' => false,
+ 'publicly_queryable' => true,
+ 'show_in_rest' => false,
+ ],
+ $hiddenArgs
+ );
+ }
+
+ public function testMerging(): void
+ {
+ $args = DegreeProgramPostType::public()
+ ->merge([
+ 'public' => false,
+ 'template' => [],
+ ])
+ ->args();
+
+ $this->assertFalse($args['public']);
+ $this->assertSame([], $args['template']);
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Content/TaxonomyTest.php b/vendor/rrze/fau-studium-common/tests/unit/Content/TaxonomyTest.php
new file mode 100644
index 0000000..5563fb4
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Content/TaxonomyTest.php
@@ -0,0 +1,86 @@
+ 'Degrees',
+ 'singular_name' => 'Degree',
+ 'search_items' => 'Search Degrees',
+ 'popular_items' => 'Popular Degrees',
+ 'all_items' => 'All Degrees',
+ 'parent_item' => 'Parent Degree',
+ 'parent_item_colon' => 'Parent Degree:',
+ 'edit_item' => 'Edit Degree',
+ 'view_item' => 'View Degree',
+ 'update_item' => 'Update Degree',
+ 'add_new_item' => 'Add New Degree',
+ 'new_item_name' => 'New Degree Name',
+ 'separate_items_with_commas' => 'Separate Degrees with commas',
+ 'add_or_remove_items' => 'Add or remove Degrees',
+ 'choose_from_most_used' => 'Choose from the most used Degrees',
+ 'not_found' => 'No Degrees found.',
+ 'no_terms' => 'No Degrees',
+ 'filter_by_item' => 'Filter by Degree',
+ 'items_list_navigation' => 'Degrees list navigation',
+ 'items_list' => 'Degrees list',
+ 'back_to_items' => '← Go to Degrees',
+ 'item_link' => 'Degree Link',
+ 'item_link_description' => 'A link to a Degree.',
+ ];
+
+ $this->assertSame(
+ [
+ 'label' => 'Degrees',
+ 'labels' => $expectedLabels,
+ 'hierarchical' => true,
+ 'rest_base' => 'degree',
+ 'public' => true,
+ 'show_in_rest' => true,
+ 'meta_box_cb' => false,
+ ],
+ DegreeTaxonomy::public()->args()
+ );
+
+ $this->assertSame(
+ [
+ 'label' => 'Degrees',
+ 'labels' => $expectedLabels,
+ 'hierarchical' => true,
+ 'rest_base' => 'degree',
+ 'public' => false,
+ 'show_in_rest' => false,
+ ],
+ DegreeTaxonomy::hidden()->args()
+ );
+ }
+
+ public function testShowAdminColumn(): void
+ {
+ $args = DegreeTaxonomy::public()
+ ->showAdminColumn()
+ ->args();
+ $this->assertTrue($args['show_admin_column']);
+ }
+
+ public function testMerging(): void
+ {
+ $args = DegreeTaxonomy::public()
+ ->merge([
+ 'public' => false,
+ 'show_in_quick_edit' => true,
+ ])
+ ->args();
+
+ $this->assertFalse($args['public']);
+ $this->assertTrue($args['show_in_quick_edit']);
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/AdmissionRequirementsTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/AdmissionRequirementsTest.php
new file mode 100644
index 0000000..38e425e
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/AdmissionRequirementsTest.php
@@ -0,0 +1,111 @@
+ [
+ 'id' => 'term:5',
+ 'name' => [
+ 'id' => 'term:5',
+ 'de' => 'Name Bachelor DE',
+ 'en' => 'Name Bachelor EN',
+ ],
+ 'link_text' => [
+ 'id' => 'term:5',
+ 'de' => 'Link Text Bachelor DE',
+ 'en' => 'Link Text Bachelor EN',
+ ],
+ 'link_url' => [
+ 'id' => 'term:5',
+ 'de' => 'Link URL Bachelor DE',
+ 'en' => 'Link URL Bachelor EN',
+ ],
+ 'parent' => null,
+ 'slug' => 'name_bachelor',
+ ],
+ 'teaching_degree_higher_semester' => [
+ 'id' => 'term:6',
+ 'name' => [
+ 'id' => 'term:6',
+ 'de' => 'Name Higher Semester DE',
+ 'en' => 'Name Higher Semester EN',
+ ],
+ 'link_text' => [
+ 'id' => 'term:6',
+ 'de' => 'Link Text Higher Semester DE',
+ 'en' => 'Link Text Higher Semester EN',
+ ],
+ 'link_url' => [
+ 'id' => 'term:6',
+ 'de' => 'Link URL Higher Semester DE',
+ 'en' => 'Link URL Higher Semester EN',
+ ],
+ 'parent' => [
+ 'id' => 'term:123',
+ 'name' => [
+ 'id' => 'term:123',
+ 'de' => 'Frei',
+ 'en' => 'Free',
+ ],
+ 'link_text' => [
+ 'id' => 'term:123',
+ 'de' => '',
+ 'en' => '',
+ ],
+ 'link_url' => [
+ 'id' => 'term:123',
+ 'de' => '',
+ 'en' => '',
+ ],
+ 'parent' => null,
+ 'slug' => 'frei',
+ ],
+ 'slug' => 'name_higher_semester',
+ ],
+ 'master' => [
+ 'id' => 'term:7',
+ 'name' => [
+ 'id' => 'term:7',
+ 'de' => 'Name Master DE',
+ 'en' => 'Name Master EN',
+ ],
+ 'link_text' => [
+ 'id' => 'term:7',
+ 'de' => 'Link Text Master DE',
+ 'en' => 'Link Text Master EN',
+ ],
+ 'link_url' => [
+ 'id' => 'term:7',
+ 'de' => 'Link URL Master DE',
+ 'en' => 'Link URL Master EN',
+ ],
+ 'parent' => null,
+ 'slug' => 'name_master',
+ ],
+ ];
+
+ $sut = AdmissionRequirements::fromArray($data);
+ $this->assertSame($data, $sut->asArray());
+ $this->assertSame(
+ 'Name Bachelor EN',
+ $sut->bachelorOrTeachingDegree()->name()->asString('en')
+ );
+ $this->assertSame(
+ 'Name Higher Semester DE',
+ $sut->teachingDegreeHigherSemester()->name()->asString('de')
+ );
+ $this->assertSame(
+ 'Name Master DE',
+ $sut->master()->name()->asString('de')
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/ContentItemTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/ContentItemTest.php
new file mode 100644
index 0000000..dc7b871
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/ContentItemTest.php
@@ -0,0 +1,83 @@
+ [
+ 'id' => 'option:about',
+ 'de' => 'About Title DE',
+ 'en' => 'About Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'About Description DE',
+ 'en' => 'About Description EN',
+ ],
+ ];
+
+ $sut = ContentItem::fromArray($data);
+ $this->assertSame(
+ $data,
+ $sut->asArray(),
+ );
+
+ $this->assertSame(
+ 'About Title DE',
+ $sut->title()->inGerman(),
+ );
+ $this->assertSame(
+ 'About Description EN',
+ $sut->description()->inEnglish(),
+ );
+ }
+
+ public function testDefault(): void
+ {
+ $data = [
+ 'title' => [
+ 'id' => 'option:original',
+ 'de' => 'About DE Original',
+ 'en' => '',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'About Description DE',
+ 'en' => 'About Description EN',
+ ],
+ ];
+
+ $sut = ContentItem::fromArray($data);
+ $sut = $sut->withDefaultTitle(
+ MultilingualString::fromTranslations(
+ 'default',
+ 'About DE Default',
+ 'About EN Default'
+ )
+ );
+ $this->assertSame(
+ [
+ 'title' => [
+ 'id' => 'option:original',
+ 'de' => 'About DE Original',
+ 'en' => 'About EN Default',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'About Description DE',
+ 'en' => 'About Description EN',
+ ],
+ ],
+ $sut->asArray()
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/ContentTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/ContentTest.php
new file mode 100644
index 0000000..bba58c7
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/ContentTest.php
@@ -0,0 +1,203 @@
+assertSame($contentData, $sut->asArray());
+ $this->assertSame(
+ 'About Title DE',
+ $sut->about()->title()->asString('de')
+ );
+ $this->assertSame(
+ 'Structure Description EN',
+ $sut->structure()->description()->asString('en')
+ );
+ $this->assertSame(
+ 'Specializations Title EN',
+ $sut->specializations()->title()->asString('en')
+ );
+ $this->assertSame(
+ 'Qualities Description DE',
+ $sut->qualitiesAndSkills()->description()->asString('de')
+ );
+ $this->assertSame(
+ 'option:why_should_study',
+ $sut->whyShouldStudy()->title()->id()
+ );
+ $this->assertSame(
+ 'Career Title DE',
+ $sut->careerProspects()->title()->inGerman()
+ );
+ $this->assertSame(
+ 'Special Title EN',
+ $sut->specialFeatures()->title()->inEnglish()
+ );
+ $this->assertSame(
+ 'Testimonials Description DE',
+ $sut->testimonials()->description()->inGerman()
+ );
+ }
+
+ /**
+ * @dataProvider contentDataProvider
+ */
+ public function testMapDescriptions(array $contentData): void
+ {
+ $sut = Content::fromArray(
+ Content::mapDescriptions(
+ $contentData,
+ static fn(string $description) => '[Was processed]' . $description
+ )
+ );
+
+ $this->assertSame(
+ 'About Title DE',
+ $sut->about()->title()->asString('de')
+ );
+ $this->assertSame(
+ '[Was processed]Structure Description EN',
+ $sut->structure()->description()->asString('en')
+ );
+ $this->assertSame(
+ 'Specializations Title EN',
+ $sut->specializations()->title()->asString('en')
+ );
+ $this->assertSame(
+ '[Was processed]Qualities Description DE',
+ $sut->qualitiesAndSkills()->description()->asString('de')
+ );
+ $this->assertSame(
+ 'option:why_should_study',
+ $sut->whyShouldStudy()->title()->id()
+ );
+ $this->assertSame(
+ 'Career Title DE',
+ $sut->careerProspects()->title()->inGerman()
+ );
+ $this->assertSame(
+ 'Special Title EN',
+ $sut->specialFeatures()->title()->inEnglish()
+ );
+ $this->assertSame(
+ '[Was processed]Testimonials Description DE',
+ $sut->testimonials()->description()->inGerman()
+ );
+ }
+
+ public function contentDataProvider(): iterable
+ {
+ $data = [
+ 'about' => [
+ 'title' => [
+ 'id' => 'option:about',
+ 'de' => 'About Title DE',
+ 'en' => 'About Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'About Description DE',
+ 'en' => 'About Description EN',
+ ],
+ ],
+ 'structure' => [
+ 'title' => [
+ 'id' => 'option:structure',
+ 'de' => 'Structure Title DE',
+ 'en' => 'Structure Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'Structure Description DE',
+ 'en' => 'Structure Description EN',
+ ],
+ ],
+ 'specializations' => [
+ 'title' => [
+ 'id' => 'option:specializations',
+ 'de' => 'Specializations Title DE',
+ 'en' => 'Specializations Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'Specializations Description DE',
+ 'en' => 'Specializations Description EN',
+ ],
+ ],
+ 'qualities_and_skills' => [
+ 'title' => [
+ 'id' => 'option:qualities_and_skills',
+ 'de' => 'Qualities Title DE',
+ 'en' => 'Qualities Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'Qualities Description DE',
+ 'en' => 'Qualities Description EN',
+ ],
+ ],
+ 'why_should_study' => [
+ 'title' => [
+ 'id' => 'option:why_should_study',
+ 'de' => 'Why Title DE',
+ 'en' => 'Why Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'Why Description DE',
+ 'en' => 'Why Description EN',
+ ],
+ ],
+ 'career_prospects' => [
+ 'title' => [
+ 'id' => 'option:career_prospects',
+ 'de' => 'Career Title DE',
+ 'en' => 'Career Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'Career Description DE',
+ 'en' => 'Career Description EN',
+ ],
+ ],
+ 'special_features' => [
+ 'title' => [
+ 'id' => 'option:special_features',
+ 'de' => 'Special Title DE',
+ 'en' => 'Special Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'Special Description DE',
+ 'en' => 'Special Description EN',
+ ],
+ ],
+ 'testimonials' => [
+ 'title' => [
+ 'id' => 'option:testimonials',
+ 'de' => 'Testimonials Title DE',
+ 'en' => 'Testimonials Title EN',
+ ],
+ 'description' => [
+ 'id' => 'post_meta:23',
+ 'de' => 'Testimonials Description DE',
+ 'en' => 'Testimonials Description EN',
+ ],
+ ],
+ ];
+
+ yield 'basic_data' => [$data];
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramIdTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramIdTest.php
new file mode 100644
index 0000000..a1d52d0
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramIdTest.php
@@ -0,0 +1,18 @@
+assertSame($id, $sut->asInt());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramIdsTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramIdsTest.php
new file mode 100644
index 0000000..80f5254
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramIdsTest.php
@@ -0,0 +1,18 @@
+assertSame($array, $sut->asArray());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramTest.php
new file mode 100644
index 0000000..305df0d
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeProgramTest.php
@@ -0,0 +1,256 @@
+expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Invalid entity id.');
+ $sut = $this->createEmptyDegreeProgram(25);
+ $data = $this->fixtureData();
+ $wrongId = 12312;
+ $data['id'] = $wrongId;
+
+ $sut->publish(
+ $data,
+ new StubDataValidator(Violations::new()),
+ new StubSanitizer(),
+ );
+ }
+
+ public function testUpdateValidationFailed(): void
+ {
+ $violations = Violations::new(Violation::new('title', 'Empty title', 'empty_title'));
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid publish degree program data.');
+ $sut = $this->createEmptyDegreeProgram(25);
+ $data = $this->fixtureData();
+
+ $sut->publish(
+ $data,
+ new StubDataValidator($violations),
+ new StubSanitizer(),
+ );
+ }
+
+ public function testUpdateSuccessfully(): void
+ {
+ $sut = $this->createEmptyDegreeProgram(25);
+ $data = $this->fixtureData();
+ $sut->publish(
+ $data,
+ new StubDataValidator(Violations::new()),
+ new StubSanitizer('[Was sanitized]'),
+ );
+ $result = $sut->asArray();
+
+ $this->assertSame(
+ 25,
+ $result['id']->asInt()
+ );
+ $this->assertSame(
+ 9,
+ $result['featured_image']->id()
+ );
+ $this->assertSame(
+ 14,
+ $result['teaser_image']->id()
+ );
+ $this->assertSame(
+ 'Master of Art FAU EN',
+ $result['title']->inEnglish()
+ );
+ $this->assertSame(
+ 'Subtitle',
+ $result['subtitle']->inGerman()
+ );
+ $this->assertSame(
+ 'Winter EN',
+ $result['start']->asArrayOfStrings('en')[1]
+ );
+ $this->assertSame(
+ 'Less
',
+ $result['number_of_students']->description()
+ );
+ $this->assertSame(
+ 'German Formal',
+ $result['teaching_language']->inGerman()
+ );
+ $this->assertSame(
+ 'DE',
+ $result['attributes']->asArrayOfStrings('de')[0]
+ );
+ $this->assertSame(
+ 'One Degree',
+ $result['degree']->name()->inGerman()
+ );
+ $this->assertSame(
+ 'Link Faculty Math EN',
+ $result['faculty']->asArray()[0]['link_text']['en']
+ );
+ $this->assertSame(
+ 'Study location',
+ $result['location']->asArrayOfStrings('de')[0]
+ );
+ $this->assertSame(
+ 'Subject Bio EN',
+ $result['subject_groups']->asArrayOfStrings('en')[0]
+ );
+ $this->assertSame(
+ [
+ "https://www.youtube.com/",
+ "https://vimeo.com/",
+ ],
+ $result['videos']->getArrayCopy()
+ );
+ $this->assertSame(
+ 'Meta description.',
+ $result['meta_description']->inGerman()
+ );
+ // The title is missing in the fixture but added in the entity constructor as the default value.
+ $this->assertSame(
+ 'Aufbau und Struktur',
+ $result['content']->structure()->title()->inGerman()
+ );
+ $this->assertSame(
+ '[Was sanitized]Structure description.',
+ $result['content']->structure()->description()->inGerman()
+ );
+ $this->assertSame(
+ 'Admission Bachelor',
+ $result['admission_requirements']->bachelorOrTeachingDegree()->name()->inGerman()
+ );
+ $this->assertSame(
+ '[Was sanitized]Master requirements.',
+ $result['content_related_master_requirements']->inGerman()
+ );
+ $this->assertSame(
+ '01.12.',
+ $result['application_deadline_winter_semester']
+ );
+ $this->assertSame(
+ '01.07.',
+ $result['application_deadline_summer_semester']
+ );
+ $this->assertSame(
+ '[Was sanitized]Notes EN.',
+ $result['details_and_notes']->inEnglish()
+ );
+ $this->assertSame(
+ '[Was sanitized]C1',
+ $result['language_skills']->inEnglish()
+ );
+ $this->assertSame(
+ '[Was sanitized]Excellent',
+ $result['language_skills_humanities_faculty']
+ );
+ $this->assertSame(
+ 'https://fau.localhost/german-language-skills-international-students-en',
+ $result['german_language_skills_for_international_students']->linkUrl()->inEnglish()
+ );
+ $this->assertSame(
+ 'Link to Start of Semester EN',
+ $result['start_of_semester']->linkText()->inEnglish()
+ );
+ $this->assertSame(
+ 'Link text Semester dates EN',
+ $result['semester_dates']->linkText()->inEnglish()
+ );
+ $this->assertSame(
+ 'Link Examinations Office EN',
+ $result['examinations_office']->linkText()->inEnglish()
+ );
+ $this->assertSame(
+ 'https://fau.localhost/examinations-regulations',
+ $result['examination_regulations']
+ );
+ $this->assertSame(
+ 'Module handbook value',
+ $result['module_handbook']
+ );
+ $this->assertSame(
+ 'https://fau.localhost/science-en',
+ $result['department']->inEnglish(),
+ );
+ $this->assertSame(
+ 'Link Student Advice and Career Service EN',
+ $result['student_advice']->linkText()->inEnglish()
+ );
+ $this->assertSame(
+ 'Link to Advice EN',
+ $result['subject_specific_advice']->linkText()->inEnglish()
+ );
+ $this->assertSame(
+ 'Link Counseling and Service Centers at FAU EN',
+ $result['service_centers']->linkText()->inEnglish()
+ );
+ $this->assertSame(
+ 'Info Brochure 2023',
+ $result['info_brochure']
+ );
+ $this->assertSame(
+ 'https://fau.localhost/semester-fee',
+ $result['semester_fee']->linkUrl()->inGerman()
+ );
+ $this->assertSame('EUR 1000', $result['degree_program_fees']->inEnglish());
+ $this->assertSame(
+ 'Opportunities for spending time abroad',
+ $result['abroad_opportunities']->name()->inGerman()
+ );
+ $this->assertSame(
+ 'Keyword 1 EN',
+ $result['keywords']->asArrayOfStrings('en')[0]
+ );
+ $this->assertSame(
+ 'https://fau.localhost/biology',
+ $result['area_of_study']->asArray()[0]['link_url']['de']
+ );
+ $this->assertSame(
+ [26, 28],
+ $result['combinations']->asArray()
+ );
+ $this->assertSame(
+ [26],
+ $result['limited_combinations']->asArray()
+ );
+ $this->assertSame(
+ [26, 28],
+ $result['combinations_changeset']->added()
+ );
+ $this->assertSame(
+ [],
+ $result['combinations_changeset']->removed()
+ );
+ $this->assertSame(
+ [26],
+ $result['limited_combinations_changeset']->added()
+ );
+ $this->assertSame(
+ [],
+ $result['limited_combinations_changeset']->removed()
+ );
+ $this->assertSame(
+ 'Notes for international applicants EN',
+ $result['notes_for_international_applicants']->name()->inEnglish()
+ );
+ $this->assertSame(
+ 'Students\' Union/Student Initiatives',
+ $result['student_initiatives']->name()->inGerman()
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeTest.php
new file mode 100644
index 0000000..b787ece
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/DegreeTest.php
@@ -0,0 +1,78 @@
+assertSame(
+ '{"id":"","name":{"id":"","de":"","en":""},"abbreviation":{"id":"","de":"","en":""},"parent":null}',
+ json_encode($sut)
+ );
+ }
+
+ public function testFromArray(): void
+ {
+ $data = [
+ 'id' => 'term:6',
+ 'name' => [
+ 'id' => 'term:5',
+ 'de' => 'Lehramt Mittelschule',
+ 'en' => 'Teaching secondary education',
+ ],
+ 'abbreviation' => [
+ 'id' => 'term:5',
+ 'de' => 'LM',
+ 'en' => 'TSE',
+ ],
+ 'parent' => [
+ 'id' => 'term:26',
+ 'name' => [
+ 'id' => 'term:26',
+ 'de' => 'Bachelorstudiengänge',
+ 'en' => 'Bachelor Degrees',
+ ],
+ 'abbreviation' => [
+ 'id' => 'term:26',
+ 'de' => 'LM',
+ 'en' => 'TSE',
+ ],
+ 'parent' => null,
+ ],
+ ];
+
+ $sut = Degree::fromArray($data);
+ $this->assertSame($data, $sut->asArray());
+ $this->assertSame(
+ 'Lehramt Mittelschule',
+ $sut->name()->inGerman()
+ );
+ $this->assertSame(
+ 'Teaching secondary education',
+ $sut->name()->inEnglish()
+ );
+ $this->assertSame(
+ 'LM',
+ $sut->abbreviation()->inGerman()
+ );
+ $this->assertSame(
+ 'TSE',
+ $sut->abbreviation()->inEnglish()
+ );
+ $this->assertSame(
+ 'Bachelorstudiengänge',
+ $sut->parent()->name()->inGerman()
+ );
+ $this->assertSame(
+ 'Bachelor Degrees',
+ $sut->parent()->name()->inEnglish()
+ );
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/ImageTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/ImageTest.php
new file mode 100644
index 0000000..2ea96b6
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/ImageTest.php
@@ -0,0 +1,29 @@
+assertEmpty($sut->id());
+ $this->assertEmpty($sut->url());
+ }
+
+ public function testFromArray(): void
+ {
+ $array = [
+ 'id' => 5,
+ 'url' => 'https://fau.localhost/wp-content/uploads/2022/12/abstract-1-1528080.jpg',
+ ];
+
+ $sut = Image::fromArray($array);
+ $this->assertSame($array, $sut->asArray());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualLinkTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualLinkTest.php
new file mode 100644
index 0000000..3ed4d40
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualLinkTest.php
@@ -0,0 +1,36 @@
+ 'term:11',
+ 'name' => [
+ 'id' => 'term:11',
+ 'de' => 'Faculty Math',
+ 'en' => 'Faculty Math EN',
+ ],
+ 'link_text' => [
+ 'id' => 'term:11',
+ 'de' => 'Link Faculty Math',
+ 'en' => 'Link Faculty Math EN',
+ ],
+ 'link_url' => [
+ 'id' => 'term:11',
+ 'de' => 'https://fau.localhost/faculty-math',
+ 'en' => 'https://fau.localhost/faculty-math-en',
+ ],
+ ];
+
+ $sut = MultilingualLink::fromArray($array);
+ $this->assertSame($array, $sut->asArray());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualLinksTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualLinksTest.php
new file mode 100644
index 0000000..ee25f77
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualLinksTest.php
@@ -0,0 +1,56 @@
+ 'term:3',
+ 'name' => [
+ 'id' => 'term:3',
+ 'de' => 'Biology',
+ 'en' => 'Biology EN',
+ ],
+ 'link_text' => [
+ 'id' => 'term:3',
+ 'de' => 'Link Biology',
+ 'en' => 'Link Biology',
+ ],
+ 'link_url' => [
+ 'id' => 'term:3',
+ 'de' => 'https://fau.localhost/biology',
+ 'en' => 'https://fau.localhost/biology-en',
+ ],
+ ],
+ [
+ 'id' => 'term:38',
+ 'name' => [
+ 'id' => 'term:38',
+ 'de' => 'Math',
+ 'en' => 'Math EN',
+ ],
+ 'link_text' => [
+ 'id' => 'term:38',
+ 'de' => 'Link Math',
+ 'en' => 'Link Math EN',
+ ],
+ 'link_url' => [
+ 'id' => 'term:38',
+ 'de' => 'https://fau.localhost/biology-math',
+ 'en' => 'https://fau.localhost/biology-math-en',
+ ],
+ ],
+ ];
+
+ $sut = MultilingualLinks::fromArray($array);
+ $this->assertSame($array, $sut->asArray());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualListTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualListTest.php
new file mode 100644
index 0000000..cb58da6
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualListTest.php
@@ -0,0 +1,30 @@
+ 'term:17',
+ 'de' => 'Keyword 1',
+ 'en' => 'Keyword 1 EN',
+ ],
+ [
+ 'id' => 'term:18',
+ 'de' => 'Keyword 2',
+ 'en' => 'Keyword 2 EN',
+ ],
+ ];
+
+ $sut = MultilingualList::fromArray($array);
+ $this->assertSame($array, $sut->asArray());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualStringTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualStringTest.php
new file mode 100644
index 0000000..fa04487
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/MultilingualStringTest.php
@@ -0,0 +1,82 @@
+assertEmpty($sut->id());
+ $this->assertEmpty($sut->inGerman());
+ $this->assertEmpty($sut->inEnglish());
+ }
+
+ public function testFromArray(): void
+ {
+ $array = [
+ 'id' => 'term:17',
+ 'de' => 'Keyword 1',
+ 'en' => 'Keyword 1 EN',
+ ];
+ $sut = MultilingualString::fromArray($array);
+ $this->assertSame($array, $sut->asArray());
+ }
+
+ public function testFromTranslations(): void
+ {
+ $array = [
+ 'id' => 'term:17',
+ 'de' => 'Keyword 1',
+ 'en' => 'Keyword 1 EN',
+ ];
+ $sut = MultilingualString::fromTranslations(...$array);
+ $this->assertSame($array, $sut->asArray());
+ $this->assertSame('term:17', $sut->id());
+ $this->assertSame('Keyword 1', $sut->inGerman());
+ $this->assertSame('Keyword 1', $sut->asString('de'));
+ $this->assertSame('Keyword 1 EN', $sut->inEnglish());
+ $this->assertSame('Keyword 1 EN', $sut->asString('en'));
+ }
+
+ public function testDefault(): void
+ {
+ $sut = MultilingualString::fromTranslations(
+ 'term:17',
+ '',
+ 'Keyword 1 EN'
+ )->mergeWithDefault(
+ MultilingualString::fromTranslations(
+ 'default',
+ 'Default Keyword',
+ 'Default Keyword EN'
+ )
+ );
+ $this->assertSame('term:17', $sut->id());
+ $this->assertSame('Default Keyword', $sut->inGerman());
+ $this->assertSame('Keyword 1 EN', $sut->inEnglish());
+ }
+
+ public function testMapTranslations(): void
+ {
+ $array = [
+ 'id' => 'term:17',
+ 'de' => 'Keyword 1',
+ 'en' => 'Keyword 1 EN',
+ ];
+ $data = MultilingualString::mapTranslations(
+ $array,
+ (static fn(string $translation) => '[Was processed]' . $translation)
+ );
+ $sut = MultilingualString::fromArray($data);
+
+ $this->assertSame('term:17', $sut->id());
+ $this->assertSame('[Was processed]Keyword 1', $sut->inGerman());
+ $this->assertSame('[Was processed]Keyword 1 EN', $sut->inEnglish());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/NumberOfStudentsTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/NumberOfStudentsTest.php
new file mode 100644
index 0000000..8b660eb
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/NumberOfStudentsTest.php
@@ -0,0 +1,26 @@
+ 'term:16',
+ 'name' => '>200',
+ 'description' => 'Many',
+ ];
+
+ $sut = NumberOfStudents::fromArray($array);
+ $this->assertSame($array, $sut->asArray());
+ $this->assertSame('Many', $sut->description());
+ $this->assertSame('>200', $sut->asString());
+ $this->assertSame('term:16', $sut->id());
+ }
+}
diff --git a/vendor/rrze/fau-studium-common/tests/unit/Domain/ViolationsTest.php b/vendor/rrze/fau-studium-common/tests/unit/Domain/ViolationsTest.php
new file mode 100644
index 0000000..66aad03
--- /dev/null
+++ b/vendor/rrze/fau-studium-common/tests/unit/Domain/ViolationsTest.php
@@ -0,0 +1,36 @@
+add(
+ Violation::new('path1', 'message1', 'code1'),
+ Violation::new('path2', 'message2', 'code2'),
+ );
+
+ $this->assertSame($violations['path1']->path(), 'path1');
+ $this->assertCount(2, $violations);
+ $this->assertSame([
+ 'path1' => [
+ 'path' => 'path1',
+ 'errorMessage' => 'message1',
+ 'errorCode' => 'code1',
+ ],
+ 'path2' => [
+ 'path' => 'path2',
+ 'errorMessage' => 'message2',
+ 'errorCode' => 'code2',
+ ],
+ ], $violations->asArray());
+ }
+}
diff --git a/vendor/webmozart/assert/CHANGELOG.md b/vendor/webmozart/assert/CHANGELOG.md
new file mode 100644
index 0000000..56c8011
--- /dev/null
+++ b/vendor/webmozart/assert/CHANGELOG.md
@@ -0,0 +1,207 @@
+Changelog
+=========
+
+## UNRELEASED
+
+## 1.11.0
+
+### Added
+
+* Added explicit (non magic) `allNullOr*` methods, with `@psalm-assert` annotations, for better Psalm support.
+
+### Changed
+
+* Trait methods will now check the assertion themselves, instead of using `__callStatic`
+* `isList` will now deal correctly with (modified) lists that contain `NaN`
+* `reportInvalidArgument` now has a return type of `never`.
+
+### Removed
+
+* Removed `symfony/polyfill-ctype` as a dependency, and require `ext-cytpe` instead.
+ * You can still require the `symfony/polyfill-ctype` in your project if you need it, as it provides `ext-ctype`
+
+## 1.10.0
+
+### Added
+
+* On invalid assertion, we throw a `Webmozart\Assert\InvalidArgumentException`
+* Added `Assert::positiveInteger()`
+
+### Changed
+
+* Using a trait with real implementations of `all*()` and `nullOr*()` methods to improve psalm compatibility.
+
+### Removed
+
+* Support for PHP <7.2
+
+## 1.9.1
+
+## Fixed
+
+* provisional support for PHP 8.0
+
+## 1.9.0
+
+* added better Psalm support for `all*` & `nullOr*` methods
+* These methods are now understood by Psalm through a mixin. You may need a newer version of Psalm in order to use this
+* added `@psalm-pure` annotation to `Assert::notFalse()`
+* added more `@psalm-assert` annotations where appropriate
+
+## Changed
+
+* the `all*` & `nullOr*` methods are now declared on an interface, instead of `@method` annotations.
+This interface is linked to the `Assert` class with a `@mixin` annotation. Most IDE's have supported this
+for a long time, and you should not lose any autocompletion capabilities. PHPStan has supported this since
+version `0.12.20`. This package is marked incompatible (with a composer conflict) with phpstan version prior to that.
+If you do not use PHPStan than this does not matter.
+
+## 1.8.0
+
+### Added
+
+* added `Assert::notStartsWith()`
+* added `Assert::notEndsWith()`
+* added `Assert::inArray()`
+* added `@psalm-pure` annotations to pure assertions
+
+### Fixed
+
+* Exception messages of comparisons between `DateTime(Immutable)` objects now display their date & time.
+* Custom Exception messages for `Assert::count()` now use the values to render the exception message.
+
+## 1.7.0 (2020-02-14)
+
+### Added
+
+* added `Assert::notFalse()`
+* added `Assert::isAOf()`
+* added `Assert::isAnyOf()`
+* added `Assert::isNotA()`
+
+## 1.6.0 (2019-11-24)
+
+### Added
+
+* added `Assert::validArrayKey()`
+* added `Assert::isNonEmptyList()`
+* added `Assert::isNonEmptyMap()`
+* added `@throws InvalidArgumentException` annotations to all methods that throw.
+* added `@psalm-assert` for the list type to the `isList` assertion.
+
+### Fixed
+
+* `ResourceBundle` & `SimpleXMLElement` now pass the `isCountable` assertions.
+They are countable, without implementing the `Countable` interface.
+* The doc block of `range` now has the proper variables.
+* An empty array will now pass `isList` and `isMap`. As it is a valid form of both.
+If a non-empty variant is needed, use `isNonEmptyList` or `isNonEmptyMap`.
+
+### Changed
+
+* Removed some `@psalm-assert` annotations, that were 'side effect' assertions See:
+ * [#144](https://github.com/webmozart/assert/pull/144)
+ * [#145](https://github.com/webmozart/assert/issues/145)
+ * [#146](https://github.com/webmozart/assert/pull/146)
+ * [#150](https://github.com/webmozart/assert/pull/150)
+* If you use Psalm, the minimum version needed is `3.6.0`. Which is enforced through a composer conflict.
+If you don't use Psalm, then this has no impact.
+
+## 1.5.0 (2019-08-24)
+
+### Added
+
+* added `Assert::uniqueValues()`
+* added `Assert::unicodeLetters()`
+* added: `Assert::email()`
+* added support for [Psalm](https://github.com/vimeo/psalm), by adding `@psalm-assert` annotations where appropriate.
+
+### Fixed
+
+* `Assert::endsWith()` would not give the correct result when dealing with a multibyte suffix.
+* `Assert::length(), minLength, maxLength, lengthBetween` would not give the correct result when dealing with multibyte characters.
+
+**NOTE**: These 2 changes may break your assertions if you relied on the fact that multibyte characters didn't behave correctly.
+
+### Changed
+
+* The names of some variables have been updated to better reflect what they are.
+* All function calls are now in their FQN form, slightly increasing performance.
+* Tests are now properly ran against HHVM-3.30 and PHP nightly.
+
+### Deprecation
+
+* deprecated `Assert::isTraversable()` in favor of `Assert::isIterable()`
+ * This was already done in 1.3.0, but it was only done through a silenced `trigger_error`. It is now annotated as well.
+
+## 1.4.0 (2018-12-25)
+
+### Added
+
+* added `Assert::ip()`
+* added `Assert::ipv4()`
+* added `Assert::ipv6()`
+* added `Assert::notRegex()`
+* added `Assert::interfaceExists()`
+* added `Assert::isList()`
+* added `Assert::isMap()`
+* added polyfill for ctype
+
+### Fixed
+
+* Special case when comparing objects implementing `__toString()`
+
+## 1.3.0 (2018-01-29)
+
+### Added
+
+* added `Assert::minCount()`
+* added `Assert::maxCount()`
+* added `Assert::countBetween()`
+* added `Assert::isCountable()`
+* added `Assert::notWhitespaceOnly()`
+* added `Assert::natural()`
+* added `Assert::notContains()`
+* added `Assert::isArrayAccessible()`
+* added `Assert::isInstanceOfAny()`
+* added `Assert::isIterable()`
+
+### Fixed
+
+* `stringNotEmpty` will no longer report "0" is an empty string
+
+### Deprecation
+
+* deprecated `Assert::isTraversable()` in favor of `Assert::isIterable()`
+
+## 1.2.0 (2016-11-23)
+
+ * added `Assert::throws()`
+ * added `Assert::count()`
+ * added extension point `Assert::reportInvalidArgument()` for custom subclasses
+
+## 1.1.0 (2016-08-09)
+
+ * added `Assert::object()`
+ * added `Assert::propertyExists()`
+ * added `Assert::propertyNotExists()`
+ * added `Assert::methodExists()`
+ * added `Assert::methodNotExists()`
+ * added `Assert::uuid()`
+
+## 1.0.2 (2015-08-24)
+
+ * integrated Style CI
+ * add tests for minimum package dependencies on Travis CI
+
+## 1.0.1 (2015-05-12)
+
+ * added support for PHP 5.3.3
+
+## 1.0.0 (2015-05-12)
+
+ * first stable release
+
+## 1.0.0-beta (2015-03-19)
+
+ * first beta release
diff --git a/vendor/webmozart/assert/LICENSE b/vendor/webmozart/assert/LICENSE
new file mode 100644
index 0000000..9e2e307
--- /dev/null
+++ b/vendor/webmozart/assert/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Bernhard Schussek
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/webmozart/assert/README.md b/vendor/webmozart/assert/README.md
new file mode 100644
index 0000000..3b2397a
--- /dev/null
+++ b/vendor/webmozart/assert/README.md
@@ -0,0 +1,287 @@
+Webmozart Assert
+================
+
+[![Latest Stable Version](https://poser.pugx.org/webmozart/assert/v/stable.svg)](https://packagist.org/packages/webmozart/assert)
+[![Total Downloads](https://poser.pugx.org/webmozart/assert/downloads.svg)](https://packagist.org/packages/webmozart/assert)
+
+This library contains efficient assertions to test the input and output of
+your methods. With these assertions, you can greatly reduce the amount of coding
+needed to write a safe implementation.
+
+All assertions in the [`Assert`] class throw an `Webmozart\Assert\InvalidArgumentException` if
+they fail.
+
+FAQ
+---
+
+**What's the difference to [beberlei/assert]?**
+
+This library is heavily inspired by Benjamin Eberlei's wonderful [assert package],
+but fixes a usability issue with error messages that can't be fixed there without
+breaking backwards compatibility.
+
+This package features usable error messages by default. However, you can also
+easily write custom error messages:
+
+```
+Assert::string($path, 'The path is expected to be a string. Got: %s');
+```
+
+In [beberlei/assert], the ordering of the `%s` placeholders is different for
+every assertion. This package, on the contrary, provides consistent placeholder
+ordering for all assertions:
+
+* `%s`: The tested value as string, e.g. `"/foo/bar"`.
+* `%2$s`, `%3$s`, ...: Additional assertion-specific values, e.g. the
+ minimum/maximum length, allowed values, etc.
+
+Check the source code of the assertions to find out details about the additional
+available placeholders.
+
+Installation
+------------
+
+Use [Composer] to install the package:
+
+```bash
+composer require webmozart/assert
+```
+
+Example
+-------
+
+```php
+use Webmozart\Assert\Assert;
+
+class Employee
+{
+ public function __construct($id)
+ {
+ Assert::integer($id, 'The employee ID must be an integer. Got: %s');
+ Assert::greaterThan($id, 0, 'The employee ID must be a positive integer. Got: %s');
+ }
+}
+```
+
+If you create an employee with an invalid ID, an exception is thrown:
+
+```php
+new Employee('foobar');
+// => Webmozart\Assert\InvalidArgumentException:
+// The employee ID must be an integer. Got: string
+
+new Employee(-10);
+// => Webmozart\Assert\InvalidArgumentException:
+// The employee ID must be a positive integer. Got: -10
+```
+
+Assertions
+----------
+
+The [`Assert`] class provides the following assertions:
+
+### Type Assertions
+
+Method | Description
+-------------------------------------------------------- | --------------------------------------------------
+`string($value, $message = '')` | Check that a value is a string
+`stringNotEmpty($value, $message = '')` | Check that a value is a non-empty string
+`integer($value, $message = '')` | Check that a value is an integer
+`integerish($value, $message = '')` | Check that a value casts to an integer
+`positiveInteger($value, $message = '')` | Check that a value is a positive (non-zero) integer
+`float($value, $message = '')` | Check that a value is a float
+`numeric($value, $message = '')` | Check that a value is numeric
+`natural($value, $message= ''')` | Check that a value is a non-negative integer
+`boolean($value, $message = '')` | Check that a value is a boolean
+`scalar($value, $message = '')` | Check that a value is a scalar
+`object($value, $message = '')` | Check that a value is an object
+`resource($value, $type = null, $message = '')` | Check that a value is a resource
+`isCallable($value, $message = '')` | Check that a value is a callable
+`isArray($value, $message = '')` | Check that a value is an array
+`isTraversable($value, $message = '')` (deprecated) | Check that a value is an array or a `\Traversable`
+`isIterable($value, $message = '')` | Check that a value is an array or a `\Traversable`
+`isCountable($value, $message = '')` | Check that a value is an array or a `\Countable`
+`isInstanceOf($value, $class, $message = '')` | Check that a value is an `instanceof` a class
+`isInstanceOfAny($value, array $classes, $message = '')` | Check that a value is an `instanceof` at least one class on the array of classes
+`notInstanceOf($value, $class, $message = '')` | Check that a value is not an `instanceof` a class
+`isAOf($value, $class, $message = '')` | Check that a value is of the class or has one of its parents
+`isAnyOf($value, array $classes, $message = '')` | Check that a value is of at least one of the classes or has one of its parents
+`isNotA($value, $class, $message = '')` | Check that a value is not of the class or has not one of its parents
+`isArrayAccessible($value, $message = '')` | Check that a value can be accessed as an array
+`uniqueValues($values, $message = '')` | Check that the given array contains unique values
+
+### Comparison Assertions
+
+Method | Description
+----------------------------------------------- | ------------------------------------------------------------------
+`true($value, $message = '')` | Check that a value is `true`
+`false($value, $message = '')` | Check that a value is `false`
+`notFalse($value, $message = '')` | Check that a value is not `false`
+`null($value, $message = '')` | Check that a value is `null`
+`notNull($value, $message = '')` | Check that a value is not `null`
+`isEmpty($value, $message = '')` | Check that a value is `empty()`
+`notEmpty($value, $message = '')` | Check that a value is not `empty()`
+`eq($value, $value2, $message = '')` | Check that a value equals another (`==`)
+`notEq($value, $value2, $message = '')` | Check that a value does not equal another (`!=`)
+`same($value, $value2, $message = '')` | Check that a value is identical to another (`===`)
+`notSame($value, $value2, $message = '')` | Check that a value is not identical to another (`!==`)
+`greaterThan($value, $value2, $message = '')` | Check that a value is greater than another
+`greaterThanEq($value, $value2, $message = '')` | Check that a value is greater than or equal to another
+`lessThan($value, $value2, $message = '')` | Check that a value is less than another
+`lessThanEq($value, $value2, $message = '')` | Check that a value is less than or equal to another
+`range($value, $min, $max, $message = '')` | Check that a value is within a range
+`inArray($value, array $values, $message = '')` | Check that a value is one of a list of values
+`oneOf($value, array $values, $message = '')` | Check that a value is one of a list of values (alias of `inArray`)
+
+### String Assertions
+
+You should check that a value is a string with `Assert::string()` before making
+any of the following assertions.
+
+Method | Description
+--------------------------------------------------- | -----------------------------------------------------------------
+`contains($value, $subString, $message = '')` | Check that a string contains a substring
+`notContains($value, $subString, $message = '')` | Check that a string does not contain a substring
+`startsWith($value, $prefix, $message = '')` | Check that a string has a prefix
+`notStartsWith($value, $prefix, $message = '')` | Check that a string does not have a prefix
+`startsWithLetter($value, $message = '')` | Check that a string starts with a letter
+`endsWith($value, $suffix, $message = '')` | Check that a string has a suffix
+`notEndsWith($value, $suffix, $message = '')` | Check that a string does not have a suffix
+`regex($value, $pattern, $message = '')` | Check that a string matches a regular expression
+`notRegex($value, $pattern, $message = '')` | Check that a string does not match a regular expression
+`unicodeLetters($value, $message = '')` | Check that a string contains Unicode letters only
+`alpha($value, $message = '')` | Check that a string contains letters only
+`digits($value, $message = '')` | Check that a string contains digits only
+`alnum($value, $message = '')` | Check that a string contains letters and digits only
+`lower($value, $message = '')` | Check that a string contains lowercase characters only
+`upper($value, $message = '')` | Check that a string contains uppercase characters only
+`length($value, $length, $message = '')` | Check that a string has a certain number of characters
+`minLength($value, $min, $message = '')` | Check that a string has at least a certain number of characters
+`maxLength($value, $max, $message = '')` | Check that a string has at most a certain number of characters
+`lengthBetween($value, $min, $max, $message = '')` | Check that a string has a length in the given range
+`uuid($value, $message = '')` | Check that a string is a valid UUID
+`ip($value, $message = '')` | Check that a string is a valid IP (either IPv4 or IPv6)
+`ipv4($value, $message = '')` | Check that a string is a valid IPv4
+`ipv6($value, $message = '')` | Check that a string is a valid IPv6
+`email($value, $message = '')` | Check that a string is a valid e-mail address
+`notWhitespaceOnly($value, $message = '')` | Check that a string contains at least one non-whitespace character
+
+### File Assertions
+
+Method | Description
+----------------------------------- | --------------------------------------------------
+`fileExists($value, $message = '')` | Check that a value is an existing path
+`file($value, $message = '')` | Check that a value is an existing file
+`directory($value, $message = '')` | Check that a value is an existing directory
+`readable($value, $message = '')` | Check that a value is a readable path
+`writable($value, $message = '')` | Check that a value is a writable path
+
+### Object Assertions
+
+Method | Description
+----------------------------------------------------- | --------------------------------------------------
+`classExists($value, $message = '')` | Check that a value is an existing class name
+`subclassOf($value, $class, $message = '')` | Check that a class is a subclass of another
+`interfaceExists($value, $message = '')` | Check that a value is an existing interface name
+`implementsInterface($value, $class, $message = '')` | Check that a class implements an interface
+`propertyExists($value, $property, $message = '')` | Check that a property exists in a class/object
+`propertyNotExists($value, $property, $message = '')` | Check that a property does not exist in a class/object
+`methodExists($value, $method, $message = '')` | Check that a method exists in a class/object
+`methodNotExists($value, $method, $message = '')` | Check that a method does not exist in a class/object
+
+### Array Assertions
+
+Method | Description
+-------------------------------------------------- | ------------------------------------------------------------------
+`keyExists($array, $key, $message = '')` | Check that a key exists in an array
+`keyNotExists($array, $key, $message = '')` | Check that a key does not exist in an array
+`validArrayKey($key, $message = '')` | Check that a value is a valid array key (int or string)
+`count($array, $number, $message = '')` | Check that an array contains a specific number of elements
+`minCount($array, $min, $message = '')` | Check that an array contains at least a certain number of elements
+`maxCount($array, $max, $message = '')` | Check that an array contains at most a certain number of elements
+`countBetween($array, $min, $max, $message = '')` | Check that an array has a count in the given range
+`isList($array, $message = '')` | Check that an array is a non-associative list
+`isNonEmptyList($array, $message = '')` | Check that an array is a non-associative list, and not empty
+`isMap($array, $message = '')` | Check that an array is associative and has strings as keys
+`isNonEmptyMap($array, $message = '')` | Check that an array is associative and has strings as keys, and is not empty
+
+### Function Assertions
+
+Method | Description
+------------------------------------------- | -----------------------------------------------------------------------------------------------------
+`throws($closure, $class, $message = '')` | Check that a function throws a certain exception. Subclasses of the exception class will be accepted.
+
+### Collection Assertions
+
+All of the above assertions can be prefixed with `all*()` to test the contents
+of an array or a `\Traversable`:
+
+```php
+Assert::allIsInstanceOf($employees, 'Acme\Employee');
+```
+
+### Nullable Assertions
+
+All of the above assertions can be prefixed with `nullOr*()` to run the
+assertion only if it the value is not `null`:
+
+```php
+Assert::nullOrString($middleName, 'The middle name must be a string or null. Got: %s');
+```
+
+### Extending Assert
+
+The `Assert` class comes with a few methods, which can be overridden to change the class behaviour. You can also extend it to
+add your own assertions.
+
+#### Overriding methods
+
+Overriding the following methods in your assertion class allows you to change the behaviour of the assertions:
+
+* `public static function __callStatic($name, $arguments)`
+ * This method is used to 'create' the `nullOr` and `all` versions of the assertions.
+* `protected static function valueToString($value)`
+ * This method is used for error messages, to convert the value to a string value for displaying. You could use this for representing a value object with a `__toString` method for example.
+* `protected static function typeToString($value)`
+ * This method is used for error messages, to convert the a value to a string representing its type.
+* `protected static function strlen($value)`
+ * This method is used to calculate string length for relevant methods, using the `mb_strlen` if available and useful.
+* `protected static function reportInvalidArgument($message)`
+ * This method is called when an assertion fails, with the specified error message. Here you can throw your own exception, or log something.
+
+## Static analysis support
+
+Where applicable, assertion functions are annotated to support Psalm's
+[Assertion syntax](https://psalm.dev/docs/annotating_code/assertion_syntax/).
+A dedicated [PHPStan Plugin](https://github.com/phpstan/phpstan-webmozart-assert) is
+required for proper type support.
+
+Authors
+-------
+
+* [Bernhard Schussek] a.k.a. [@webmozart]
+* [The Community Contributors]
+
+Contribute
+----------
+
+Contributions to the package are always welcome!
+
+* Report any bugs or issues you find on the [issue tracker].
+* You can grab the source code at the package's [Git repository].
+
+License
+-------
+
+All contents of this package are licensed under the [MIT license].
+
+[beberlei/assert]: https://github.com/beberlei/assert
+[assert package]: https://github.com/beberlei/assert
+[Composer]: https://getcomposer.org
+[Bernhard Schussek]: https://webmozarts.com
+[The Community Contributors]: https://github.com/webmozart/assert/graphs/contributors
+[issue tracker]: https://github.com/webmozart/assert/issues
+[Git repository]: https://github.com/webmozart/assert
+[@webmozart]: https://twitter.com/webmozart
+[MIT license]: LICENSE
+[`Assert`]: src/Assert.php
diff --git a/vendor/webmozart/assert/composer.json b/vendor/webmozart/assert/composer.json
new file mode 100644
index 0000000..b340452
--- /dev/null
+++ b/vendor/webmozart/assert/composer.json
@@ -0,0 +1,43 @@
+{
+ "name": "webmozart/assert",
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "license": "MIT",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "ext-ctype": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.13"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<0.12.20",
+ "vimeo/psalm": "<4.6.1 || 4.6.2"
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Webmozart\\Assert\\Tests\\": "tests/",
+ "Webmozart\\Assert\\Bin\\": "bin/src"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ }
+}
diff --git a/vendor/webmozart/assert/src/Assert.php b/vendor/webmozart/assert/src/Assert.php
new file mode 100644
index 0000000..db1f3a5
--- /dev/null
+++ b/vendor/webmozart/assert/src/Assert.php
@@ -0,0 +1,2080 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Webmozart\Assert;
+
+use ArrayAccess;
+use BadMethodCallException;
+use Closure;
+use Countable;
+use DateTime;
+use DateTimeImmutable;
+use Exception;
+use ResourceBundle;
+use SimpleXMLElement;
+use Throwable;
+use Traversable;
+
+/**
+ * Efficient assertions to validate the input/output of your methods.
+ *
+ * @since 1.0
+ *
+ * @author Bernhard Schussek
+ */
+class Assert
+{
+ use Mixin;
+
+ /**
+ * @psalm-pure
+ * @psalm-assert string $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function string($value, $message = '')
+ {
+ if (!\is_string($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a string. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert non-empty-string $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function stringNotEmpty($value, $message = '')
+ {
+ static::string($value, $message);
+ static::notEq($value, '', $message);
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert int $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function integer($value, $message = '')
+ {
+ if (!\is_int($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected an integer. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert numeric $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function integerish($value, $message = '')
+ {
+ if (!\is_numeric($value) || $value != (int) $value) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected an integerish value. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert positive-int $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function positiveInteger($value, $message = '')
+ {
+ if (!(\is_int($value) && $value > 0)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a positive integer. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert float $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function float($value, $message = '')
+ {
+ if (!\is_float($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a float. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert numeric $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function numeric($value, $message = '')
+ {
+ if (!\is_numeric($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a numeric. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert positive-int|0 $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function natural($value, $message = '')
+ {
+ if (!\is_int($value) || $value < 0) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a non-negative integer. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert bool $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function boolean($value, $message = '')
+ {
+ if (!\is_bool($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a boolean. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert scalar $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function scalar($value, $message = '')
+ {
+ if (!\is_scalar($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a scalar. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert object $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function object($value, $message = '')
+ {
+ if (!\is_object($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected an object. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert resource $value
+ *
+ * @param mixed $value
+ * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function resource($value, $type = null, $message = '')
+ {
+ if (!\is_resource($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a resource. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+
+ if ($type && $type !== \get_resource_type($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a resource of type %2$s. Got: %s',
+ static::typeToString($value),
+ $type
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert callable $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function isCallable($value, $message = '')
+ {
+ if (!\is_callable($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a callable. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert array $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function isArray($value, $message = '')
+ {
+ if (!\is_array($value)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected an array. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert iterable $value
+ *
+ * @deprecated use "isIterable" or "isInstanceOf" instead
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function isTraversable($value, $message = '')
+ {
+ @\trigger_error(
+ \sprintf(
+ 'The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.',
+ __METHOD__
+ ),
+ \E_USER_DEPRECATED
+ );
+
+ if (!\is_array($value) && !($value instanceof Traversable)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a traversable. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert array|ArrayAccess $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function isArrayAccessible($value, $message = '')
+ {
+ if (!\is_array($value) && !($value instanceof ArrayAccess)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected an array accessible. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert countable $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function isCountable($value, $message = '')
+ {
+ if (
+ !\is_array($value)
+ && !($value instanceof Countable)
+ && !($value instanceof ResourceBundle)
+ && !($value instanceof SimpleXMLElement)
+ ) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected a countable. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-assert iterable $value
+ *
+ * @param mixed $value
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function isIterable($value, $message = '')
+ {
+ if (!\is_array($value) && !($value instanceof Traversable)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected an iterable. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-template ExpectedType of object
+ * @psalm-param class-string $class
+ * @psalm-assert ExpectedType $value
+ *
+ * @param mixed $value
+ * @param string|object $class
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function isInstanceOf($value, $class, $message = '')
+ {
+ if (!($value instanceof $class)) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected an instance of %2$s. Got: %s',
+ static::typeToString($value),
+ $class
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-template ExpectedType of object
+ * @psalm-param class-string $class
+ * @psalm-assert !ExpectedType $value
+ *
+ * @param mixed $value
+ * @param string|object $class
+ * @param string $message
+ *
+ * @throws InvalidArgumentException
+ */
+ public static function notInstanceOf($value, $class, $message = '')
+ {
+ if ($value instanceof $class) {
+ static::reportInvalidArgument(\sprintf(
+ $message ?: 'Expected an instance other than %2$s. Got: %s',
+ static::typeToString($value),
+ $class
+ ));
+ }
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-param array $classes
+ *
+ * @param mixed $value
+ * @param array