Skip to content

Commit ba2a061

Browse files
author
Sebastian BURGIN-FIX (ext)
committed
Added .pngs
1 parent 8a0c1ab commit ba2a061

7 files changed

Lines changed: 36 additions & 10 deletions

File tree

app/Support/NewsImage.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ public static function src(?string $reference, int $width): ?string
4343
return self::fromPublicId($reference, $width);
4444
}
4545

46+
/**
47+
* The og:image counterpart of an SVG hero. Social crawlers cannot render
48+
* SVG, so a same-named PNG rendered from it — see
49+
* public/images/news/placeholders/ — is used when one exists; otherwise
50+
* the caller falls back to the site default image.
51+
*/
52+
public static function ogImage(string $svgReference): ?string
53+
{
54+
if (! self::isLocalPath($svgReference) || ! str_ends_with(strtolower($svgReference), '.svg')) {
55+
return null;
56+
}
57+
58+
$png = substr($svgReference, 0, -4).'.png';
59+
60+
return is_file(public_path(ltrim($png, '/'))) ? asset(ltrim($png, '/')) : null;
61+
}
62+
4663
public static function srcset(?string $reference, int $maxWidth): ?string
4764
{
4865
if (self::src($reference, $maxWidth) === null) {

phpstan.neon.dist

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ parameters:
2121
paths:
2222
- app/Notifications/*
2323

24+
# Mockery's shouldReceive()/expects() are documented as returning a union in
25+
# which only Expectation declares the count modifiers. The calls are correct —
26+
# a mutation check confirms the expectation fails when the behaviour is removed —
27+
# but the stubs cannot express which member of the union is returned.
28+
-
29+
identifier: method.notFound
30+
message: '#Mockery\\(ExpectationInterface|HigherOrderMessage|ExpectsHigherOrderMessage)#'
31+
paths:
32+
- tests/*
33+
2434
excludePaths:
2535
# Vendor-published migration (spatie/laravel-permission) — not our code.
2636
- database/migrations/2024_06_06_100452_create_permission_tables.php
92 KB
Loading
90.6 KB
Loading
92.2 KB
Loading

resources/views/layouts/_partials/_seo.blade.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
@php
1010
// A page image can be a Cloudinary public ID, a remote URL or a path inside
1111
// public/ — only the second form is usable as-is, so NewsImage resolves it to
12-
// an absolute URL. SVG heroes (the local release placeholders) are skipped:
13-
// the social networks do not render SVG, and og:image:type below says PNG.
12+
// an absolute URL. SVG heroes (the local release placeholders) cannot be used
13+
// directly: social networks do not render SVG, and og:image:type below says
14+
// PNG. A same-named PNG rendered from the SVG is used instead when one exists.
1415
$seoImage = str_ends_with(strtolower((string) $page->image), '.svg')
15-
? null
16+
? \App\Support\NewsImage::ogImage($page->image)
1617
: \App\Support\NewsImage::src($page->image, config()->integer('seo.image_width'));
1718
1819
$seoImage ??= url(asset(config('seo.default_image')));

tests/Feature/Jobs/FetchLlmUsageJobTest.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ function spendLogsPayload(int $promptTokens): array
7979
})->group('llm-analytics');
8080

8181
it('does not clear the response cache when a single day is stored', function () {
82-
$responseCache = ResponseCache::spy();
82+
// Clearing the whole site's rendered HTML is the sync's decision, not one day's —
83+
// otherwise an hourly run wipes the response cache once per day in its window.
84+
ResponseCache::partialMock()->shouldReceive('clear')->never();
8385

8486
(new StoreLlmUsageAction)->store(collect([[
8587
'date' => '2026-07-20',
@@ -91,19 +93,15 @@ function spendLogsPayload(int $promptTokens): array
9193
'spend' => 0.5,
9294
]]));
9395

94-
// Clearing the whole site's rendered HTML is the sync's decision, not one day's —
95-
// otherwise an hourly run wipes the response cache once per day in its window.
96-
$responseCache->shouldNotHaveReceived('clear');
9796
})->group('llm-analytics');
9897

9998
it('clears the response cache once after the whole sync batch finishes', function () {
10099
Http::fake(['llm.codebar.net/spend/logs*' => Http::response(spendLogsPayload(promptTokens: 100))]);
101100

102-
$responseCache = ResponseCache::spy();
101+
// Four days are synced below; the rendered pages are dropped exactly once.
102+
ResponseCache::partialMock()->shouldReceive('clear')->once();
103103

104104
runArtisan('llm:fetch-analytics', ['--from' => '2026-07-20', '--to' => '2026-07-23'])
105105
->assertSuccessful();
106106

107-
// Four days were synced; the rendered pages are dropped exactly once.
108-
$responseCache->shouldHaveReceived('clear')->once();
109107
})->group('llm-analytics');

0 commit comments

Comments
 (0)