Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coverage-baseline
Original file line number Diff line number Diff line change
@@ -1 +1 @@
58.8
58.8
18 changes: 1 addition & 17 deletions lib/Service/Object/SaveObject/FilePropertyHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,29 +184,13 @@ public function isFileProperty($value, ?Schema $schema=null, ?string $propertyNa
$schemaProperties = $schema->getProperties() ?? [];

if (isset($schemaProperties[$propertyName]) === false) {
$this->logger->debug(
message: '[FilePropertyHandler] isFileProperty: Property not in schema',
context: ['file' => __FILE__, 'line' => __LINE__, 'app' => 'openregister', 'property' => $propertyName]
);
return false;
// Property not in schema, not a file.
return false;
}

$propertyConfig = $schemaProperties[$propertyName];
$propertyType = $propertyConfig['type'] ?? '';

$this->logger->debug(
message: '[FilePropertyHandler] isFileProperty: Checking property type',
context: [
'file' => __FILE__,
'line' => __LINE__,
'app' => 'openregister',
'property' => $propertyName,
'type' => $propertyType,
'isFile' => ($propertyType === 'file'),
]
);

// Check if it's a direct file property.
if ($propertyType === 'file') {
return true;
Expand Down
67 changes: 67 additions & 0 deletions tests/Unit/Service/Object/SaveObject/FilePropertyHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2322,4 +2322,71 @@ public function testHandleFilePropertyPropertyAutoPublishOverridesSchema(): void

$this->assertSame(70, $object['document']);
}

/**
* Schema-based detection: a property declared as `type: file` IS a file property.
*
* This path had no direct coverage. It is also the hot predicate — called once
* per property per object — so it is worth pinning: two debug log lines were
* removed from it after they filled a dev instance's disk with 112 GB of
* per-property logging during an upgrade.
*
* @return void
*/
public function testSchemaDeclaredFilePropertyIsDetected(): void
{
$schema = $this->createMock(Schema::class);
$schema->method('getProperties')->willReturn(['document' => ['type' => 'file']]);

$this->assertTrue($this->handler->isFileProperty('anything', $schema, 'document'));
}

/**
* A property the schema does not declare is not a file property.
*
* @return void
*/
public function testPropertyAbsentFromSchemaIsNotAFileProperty(): void
{
$schema = $this->createMock(Schema::class);
$schema->method('getProperties')->willReturn(['title' => ['type' => 'string']]);

$this->assertFalse($this->handler->isFileProperty('anything', $schema, 'nosuchproperty'));
}

/**
* A declared non-file property is not a file property, whatever its value.
*
* Asserted with a value that WOULD pass the value-shape heuristics, so the
* schema is shown to win over the shape rather than merely agreeing with it.
*
* @return void
*/
public function testSchemaTypeWinsOverAFileLookingValue(): void
{
$schema = $this->createMock(Schema::class);
$schema->method('getProperties')->willReturn(['title' => ['type' => 'string']]);

$this->assertFalse(
$this->handler->isFileProperty('https://example.com/files/document.pdf', $schema, 'title')
);
}

/**
* A schema with no properties at all does not crash the predicate.
*
* Uses an empty array rather than null: getProperties() is typed `array`, so
* null is not reachable — which also means the `?? []` guarding it in the
* handler is dead. Left in place; noting it here rather than changing
* unrelated code in a fix for a logging problem.
*
* @return void
*/
public function testASchemaWithNoPropertiesIsHandled(): void
{
$schema = $this->createMock(Schema::class);
$schema->method('getProperties')->willReturn([]);

$this->assertFalse($this->handler->isFileProperty('anything', $schema, 'document'));
}
}
88 changes: 88 additions & 0 deletions tests/Unit/Twig/MappingRuntimeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,92 @@ public function testExecuteMappingWithHttpUrlReference(): void

$this->runtime->executeMapping('http://local/mapping', []);
}

/**
* createSlug's exact output is load-bearing, not cosmetic.
*
* Harvest flows persist the slug as an object identifier, so a change to
* this transformation orphans every object written under the old rule. It
* was ported byte-for-byte from OpenConnector for that reason; these cases
* pin the behaviour that port preserved.
*
* @dataProvider slugProvider
*
* @param string $input The text to slugify.
* @param string $expected The expected slug.
*
* @return void
*/
public function testCreateSlugIsStable(string $input, string $expected): void
{
$this->assertSame($expected, $this->runtime->createSlug($input));
}

/**
* Slug cases, each a rule the transformation applies in order.
*
* @return array<string, array{0: string, 1: string}>
*/
public static function slugProvider(): array
{
return [
'lowercases' => ['Hello World', 'hello-world'],
'spaces to hyphens' => ['a b c', 'a-b-c'],
'underscores to hyphens' => ['a_b_c', 'a-b-c'],
'strips punctuation' => ['Hello, World!', 'hello-world'],
'collapses repeat hyphens' => ['a---b', 'a-b'],
'trims leading and trailing' => ['-abc-', 'abc'],
'keeps digits' => ['Repo 2026', 'repo-2026'],
'strips non-ascii' => ['Ruben van der Linde', 'ruben-van-der-linde'],
'empty stays empty' => ['', ''],
'punctuation only' => ['!!!', ''],
];
}

/**
* json_decode returns an associative array, under the snake_case name
* OpenConnector's stored templates call.
*
* @return void
*/
public function testJsonDecodeReturnsAnAssociativeArray(): void
{
$this->assertSame(['a' => 1, 'b' => ['c' => 2]], $this->runtime->json_decode('{"a":1,"b":{"c":2}}'));
}

/**
* Malformed JSON yields an empty array rather than a fatal.
*
* A mapping template is authored data; a typo in it must not take the whole
* run down with an uncatchable error.
*
* @return void
*/
public function testJsonDecodeOnMalformedInputIsEmpty(): void
{
$this->assertSame([], $this->runtime->json_decode('{not json'));
}

/**
* Both spellings decode identically — that is the whole point of keeping two.
*
* @return void
*/
public function testBothJsonDecodeSpellingsAgree(): void
{
$json = '{"x":[1,2,3]}';
$this->assertSame($this->runtime->json_decode($json), $this->runtime->jsonDecode($json));
}

/**
* base64 round-trips.
*
* @return void
*/
public function testBase64RoundTrips(): void
{
$this->assertSame('aGk=', $this->runtime->b64enc('hi'));
$this->assertSame('hi', $this->runtime->b64dec('aGk='));
$this->assertSame('some text', $this->runtime->b64dec($this->runtime->b64enc('some text')));
}
}
Loading