Compare commits
3 Commits
31ed52500a
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| e555413295 | |||
| 3768b82932 | |||
| d1ce73e7ff |
@@ -328,7 +328,7 @@ class ManageSuggestions extends Page implements HasTable
|
|||||||
/**
|
/**
|
||||||
* Polls a sync job's status (wire:poll.2s, see the view). On "finished",
|
* Polls a sync job's status (wire:poll.2s, see the view). On "finished",
|
||||||
* chains a reload-index call — same two-step flow shweai_backend's admin
|
* chains a reload-index call — same two-step flow shweai_backend's admin
|
||||||
* JS does (POST sync-chroma → poll → POST reload-index) — and refreshes
|
* JS does (POST sync → poll → POST reload-index) — and refreshes
|
||||||
* the table. On "failed", notifies and stops polling.
|
* the table. On "failed", notifies and stops polling.
|
||||||
*/
|
*/
|
||||||
public function pollSyncStatus(): void
|
public function pollSyncStatus(): void
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ test('deleteSelected bulk action calls batchDeleteSuggestions with just the sele
|
|||||||
|
|
||||||
test('sync sets job state and polling chains a reload-index call on finished', function () {
|
test('sync sets job state and polling chains a reload-index call on finished', function () {
|
||||||
Http::fake(['bnfexpress.test/*' => fn (Request $request) => match (true) {
|
Http::fake(['bnfexpress.test/*' => fn (Request $request) => match (true) {
|
||||||
str_contains($request->url(), '/sync-chroma/job-1') => Http::response(['status' => 'finished', 'result' => ['synced' => 3]]),
|
str_contains($request->url(), '/sync/job-1') => Http::response(['status' => 'finished', 'result' => ['synced' => 3]]),
|
||||||
str_contains($request->url(), '/sync-chroma') => Http::response(['job_id' => 'job-1'], 202),
|
str_contains($request->url(), '/sync') => Http::response(['job_id' => 'job-1'], 202),
|
||||||
str_contains($request->url(), '/reload-index') => Http::response(['trie_rebuilt' => true]),
|
str_contains($request->url(), '/reload-index') => Http::response(['trie_rebuilt' => true]),
|
||||||
default => Http::response([]),
|
default => Http::response([]),
|
||||||
}]);
|
}]);
|
||||||
@@ -144,8 +144,8 @@ test('sync sets job state and polling chains a reload-index call on finished', f
|
|||||||
|
|
||||||
test('a failed sync job notifies danger and stops polling', function () {
|
test('a failed sync job notifies danger and stops polling', function () {
|
||||||
Http::fake(['bnfexpress.test/*' => fn (Request $request) => match (true) {
|
Http::fake(['bnfexpress.test/*' => fn (Request $request) => match (true) {
|
||||||
str_contains($request->url(), '/sync-chroma/job-1') => Http::response(['status' => 'failed', 'result' => null]),
|
str_contains($request->url(), '/sync/job-1') => Http::response(['status' => 'failed', 'result' => null]),
|
||||||
str_contains($request->url(), '/sync-chroma') => Http::response(['job_id' => 'job-1'], 202),
|
str_contains($request->url(), '/sync') => Http::response(['job_id' => 'job-1'], 202),
|
||||||
default => Http::response([]),
|
default => Http::response([]),
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
@@ -182,6 +182,6 @@ test('syncEmbedding and deleteEmbedding row actions call the right per-id endpoi
|
|||||||
$test->callTableAction('syncEmbedding', 1)->assertNotified('Embedding synced');
|
$test->callTableAction('syncEmbedding', 1)->assertNotified('Embedding synced');
|
||||||
$test->callTableAction('deleteEmbedding', 1)->assertNotified('Embedding deleted');
|
$test->callTableAction('deleteEmbedding', 1)->assertNotified('Embedding deleted');
|
||||||
|
|
||||||
Http::assertSent(fn (Request $request) => str_contains($request->url(), '/admin/suggestions/1/sync-chroma') && $request->method() === 'POST');
|
Http::assertSent(fn (Request $request) => str_contains($request->url(), '/admin/suggestions/1/sync') && $request->method() === 'POST');
|
||||||
Http::assertSent(fn (Request $request) => str_contains($request->url(), '/admin/suggestions/1/chroma') && $request->method() === 'DELETE');
|
Http::assertSent(fn (Request $request) => str_contains($request->url(), '/admin/suggestions/1/embedding') && $request->method() === 'DELETE');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ class EvCompanyForm
|
|||||||
->required()
|
->required()
|
||||||
->maxLength(255),
|
->maxLength(255),
|
||||||
FileUpload::make('logo')
|
FileUpload::make('logo')
|
||||||
->image(),
|
->image()
|
||||||
|
->disk('public')
|
||||||
|
->directory('logos'),
|
||||||
Toggle::make('is_active')
|
Toggle::make('is_active')
|
||||||
->required()
|
->required()
|
||||||
->default(true),
|
->default(true),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class EvCompaniesTable
|
|||||||
return $table
|
return $table
|
||||||
->columns([
|
->columns([
|
||||||
ImageColumn::make('logo')
|
ImageColumn::make('logo')
|
||||||
|
->disk('public')
|
||||||
->circular(),
|
->circular(),
|
||||||
TextColumn::make('name')
|
TextColumn::make('name')
|
||||||
->searchable()
|
->searchable()
|
||||||
|
|||||||
@@ -78,9 +78,10 @@ class EvCompany extends Model
|
|||||||
/**
|
/**
|
||||||
* `logo` is stored as the disk-relative path Filament's FileUpload
|
* `logo` is stored as the disk-relative path Filament's FileUpload
|
||||||
* writes (e.g. "logos/xxx.png"), not a URL — API consumers need a full
|
* writes (e.g. "logos/xxx.png"), not a URL — API consumers need a full
|
||||||
* absolute URL to render it directly. Guards against the disk itself
|
* absolute URL to render it directly. Logos are always written to the
|
||||||
* already returning an absolute URL (e.g. an s3 disk), so this stays
|
* "public" disk (storage/app/public, symlinked to public/storage) so
|
||||||
* correct if the storage disk ever changes from local.
|
* the frontend can actually reach the image, regardless of what the
|
||||||
|
* app's default filesystem disk is configured to.
|
||||||
*/
|
*/
|
||||||
public function logoUrl(): Attribute
|
public function logoUrl(): Attribute
|
||||||
{
|
{
|
||||||
@@ -90,7 +91,7 @@ class EvCompany extends Model
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = Storage::disk(config('filesystems.default'))->url($this->logo);
|
$url = Storage::disk('public')->url($this->logo);
|
||||||
|
|
||||||
return str($url)->startsWith(['http://', 'https://']) ? $url : url($url);
|
return str($url)->startsWith(['http://', 'https://']) ? $url : url($url);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ test('returns the company logo as a full absolute url', function () {
|
|||||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||||
->getJson('/api/v1/companies')
|
->getJson('/api/v1/companies')
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertJsonFragment(['logo' => url(Storage::disk(config('filesystems.default'))->url($company->logo))]);
|
->assertJsonFragment(['logo' => url(Storage::disk('public')->url($company->logo))]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('returns a null logo when the company has none', function () {
|
test('returns a null logo when the company has none', function () {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
use Modules\Catalog\Filament\Resources\EvCompanies\Pages\CreateEvCompany;
|
use Modules\Catalog\Filament\Resources\EvCompanies\Pages\CreateEvCompany;
|
||||||
@@ -26,6 +27,17 @@ test('can list ev companies', function () {
|
|||||||
->assertCanSeeTableRecords($companies);
|
->assertCanSeeTableRecords($companies);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('renders the logo from the public disk in the table', function () {
|
||||||
|
Storage::fake('public');
|
||||||
|
Storage::disk('public')->put('logos/example.png', 'fake-image-contents');
|
||||||
|
|
||||||
|
$company = EvCompany::factory()->create(['logo' => 'logos/example.png']);
|
||||||
|
|
||||||
|
Livewire::test(ListEvCompanies::class)
|
||||||
|
->assertOk()
|
||||||
|
->assertSee(Storage::disk('public')->url($company->logo), escape: false);
|
||||||
|
});
|
||||||
|
|
||||||
test('can create an ev company', function () {
|
test('can create an ev company', function () {
|
||||||
$company = EvCompany::factory()->make();
|
$company = EvCompany::factory()->make();
|
||||||
|
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ class BnfexpressAdminClient
|
|||||||
*/
|
*/
|
||||||
public function syncSuggestions(): array
|
public function syncSuggestions(): array
|
||||||
{
|
{
|
||||||
return $this->request('POST', '/admin/suggestions/sync-chroma');
|
return $this->request('POST', '/admin/suggestions/sync');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,7 +290,7 @@ class BnfexpressAdminClient
|
|||||||
*/
|
*/
|
||||||
public function getSuggestionSyncStatus(string $jobId): array
|
public function getSuggestionSyncStatus(string $jobId): array
|
||||||
{
|
{
|
||||||
return $this->request('GET', "/admin/suggestions/sync-chroma/{$jobId}");
|
return $this->request('GET', "/admin/suggestions/sync/{$jobId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,7 +298,7 @@ class BnfexpressAdminClient
|
|||||||
*/
|
*/
|
||||||
public function syncOneSuggestion(int|string $id): array
|
public function syncOneSuggestion(int|string $id): array
|
||||||
{
|
{
|
||||||
return $this->request('POST', "/admin/suggestions/{$id}/sync-chroma");
|
return $this->request('POST', "/admin/suggestions/{$id}/sync");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -314,7 +314,7 @@ class BnfexpressAdminClient
|
|||||||
*/
|
*/
|
||||||
public function deleteSuggestionEmbedding(int|string $id): array
|
public function deleteSuggestionEmbedding(int|string $id): array
|
||||||
{
|
{
|
||||||
return $this->request('DELETE', "/admin/suggestions/{$id}/chroma");
|
return $this->request('DELETE', "/admin/suggestions/{$id}/embedding");
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Request plumbing ------------------------------------------------
|
// --- Request plumbing ------------------------------------------------
|
||||||
|
|||||||
@@ -175,13 +175,13 @@ test('promoteSuggestionMisses posts miss_ids with an optional lang/intent overri
|
|||||||
&& $request->body() === json_encode(['miss_ids' => [1, 2], 'lang' => 'my']));
|
&& $request->body() === json_encode(['miss_ids' => [1, 2], 'lang' => 'my']));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('syncSuggestions triggers a sync-chroma job and returns the job id', function () use ($config) {
|
test('syncSuggestions triggers a sync job and returns the job id', function () use ($config) {
|
||||||
Http::fake(['bnfexpress.test/*' => Http::response(['job_id' => 'abc-123'], 202)]);
|
Http::fake(['bnfexpress.test/*' => Http::response(['job_id' => 'abc-123'], 202)]);
|
||||||
|
|
||||||
$result = (new BnfexpressAdminClient($config))->syncSuggestions();
|
$result = (new BnfexpressAdminClient($config))->syncSuggestions();
|
||||||
|
|
||||||
expect($result)->toBe(['job_id' => 'abc-123']);
|
expect($result)->toBe(['job_id' => 'abc-123']);
|
||||||
Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestions/sync-chroma'
|
Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestions/sync'
|
||||||
&& $request->method() === 'POST');
|
&& $request->method() === 'POST');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ test('getSuggestionSyncStatus polls the job status endpoint', function () use ($
|
|||||||
$result = (new BnfexpressAdminClient($config))->getSuggestionSyncStatus('abc-123');
|
$result = (new BnfexpressAdminClient($config))->getSuggestionSyncStatus('abc-123');
|
||||||
|
|
||||||
expect($result)->toBe(['status' => 'finished', 'result' => ['synced' => 3]]);
|
expect($result)->toBe(['status' => 'finished', 'result' => ['synced' => 3]]);
|
||||||
Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestions/sync-chroma/abc-123'
|
Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestions/sync/abc-123'
|
||||||
&& $request->method() === 'GET');
|
&& $request->method() === 'GET');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -201,8 +201,8 @@ test('deleteSuggestionEmbedding signs a bodyless DELETE request', function () us
|
|||||||
(new BnfexpressAdminClient($config))->deleteSuggestionEmbedding(5);
|
(new BnfexpressAdminClient($config))->deleteSuggestionEmbedding(5);
|
||||||
|
|
||||||
Http::assertSent(function ($request) use ($config) {
|
Http::assertSent(function ($request) use ($config) {
|
||||||
return $request->url() === 'https://bnfexpress.test/admin/suggestions/5/chroma'
|
return $request->url() === 'https://bnfexpress.test/admin/suggestions/5/embedding'
|
||||||
&& $request->method() === 'DELETE'
|
&& $request->method() === 'DELETE'
|
||||||
&& assertSignedCorrectly($request, 'DELETE', '/admin/suggestions/5/chroma', '', $config['client_secret']);
|
&& assertSignedCorrectly($request, 'DELETE', '/admin/suggestions/5/embedding', '', $config['client_secret']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user