trypost/tests/Feature/Ai/Templates/TemplateContractTest.php

53 lines
1.9 KiB
PHP
Raw Permalink Normal View History

<?php
declare(strict_types=1);
use App\Ai\Templates\ImageCardTemplate;
feat(ai-templates): styles orthogonal to single/carousel; tweet-card carousel - TemplateContext gains bool $isCarousel (set from format === CAROUSEL_FORMAT in StreamPostCreation) - AiContentTemplate interface: promptView() now takes TemplateContext - ImageCardTemplate handles both single and carousel via context.isCarousel; schema() returns the correct shape per mode; assemble() routes to assembleSingle/assembleCarousel - TweetCardTemplate handles both single and carousel; promptView() returns tweet_card_carousel view when isCarousel; schema() returns {tweet_text} or {caption, slides[]}; assemble() calls forTweetCard (single) or forTweetCardCarousel (carousel) - CarouselTemplate deleted — carousel is now a format dimension each style handles, not a standalone style - AiTemplateRegistry: [ImageCardTemplate, TweetCardTemplate] only - PostImagePipeline: adds forTweetCardCarousel — renders one renderTweetCard per slide text, returns N media items - StreamPostCreation: carousel detection restored to $format === CAROUSEL_FORMAT (like main); style dispatched via AiTemplateRegistry::find($this->template); humanize skips tweet_card (single and carousel) - PostContentGenerator: promptView() call updated to pass templateContext - New prompt: resources/views/prompts/post_content/tweet_card_carousel.blade.php - Tests: TemplateContractTest and TweetCardTemplateTest updated for promptView(context) signature; AiTemplateRegistryTest asserts [image_card, tweet_card]; StreamPostCreationTest adds tweet_card carousel test; TweetCardTemplateTest covers both schema shapes and both promptView return values
2026-06-17 21:59:41 +00:00
use App\Ai\Templates\TemplateContext;
use App\Ai\Templates\TweetCardImageTemplate;
use App\Ai\Templates\TweetCardTemplate;
use App\Enums\Ai\ContentStyle;
feat(ai-templates): styles orthogonal to single/carousel; tweet-card carousel - TemplateContext gains bool $isCarousel (set from format === CAROUSEL_FORMAT in StreamPostCreation) - AiContentTemplate interface: promptView() now takes TemplateContext - ImageCardTemplate handles both single and carousel via context.isCarousel; schema() returns the correct shape per mode; assemble() routes to assembleSingle/assembleCarousel - TweetCardTemplate handles both single and carousel; promptView() returns tweet_card_carousel view when isCarousel; schema() returns {tweet_text} or {caption, slides[]}; assemble() calls forTweetCard (single) or forTweetCardCarousel (carousel) - CarouselTemplate deleted — carousel is now a format dimension each style handles, not a standalone style - AiTemplateRegistry: [ImageCardTemplate, TweetCardTemplate] only - PostImagePipeline: adds forTweetCardCarousel — renders one renderTweetCard per slide text, returns N media items - StreamPostCreation: carousel detection restored to $format === CAROUSEL_FORMAT (like main); style dispatched via AiTemplateRegistry::find($this->template); humanize skips tweet_card (single and carousel) - PostContentGenerator: promptView() call updated to pass templateContext - New prompt: resources/views/prompts/post_content/tweet_card_carousel.blade.php - Tests: TemplateContractTest and TweetCardTemplateTest updated for promptView(context) signature; AiTemplateRegistryTest asserts [image_card, tweet_card]; StreamPostCreationTest adds tweet_card carousel test; TweetCardTemplateTest covers both schema shapes and both promptView return values
2026-06-17 21:59:41 +00:00
use App\Models\Workspace;
use Illuminate\JsonSchema\JsonSchemaTypeFactory;
test('image card template exposes its identity', function () {
$t = new ImageCardTemplate;
feat(ai-templates): styles orthogonal to single/carousel; tweet-card carousel - TemplateContext gains bool $isCarousel (set from format === CAROUSEL_FORMAT in StreamPostCreation) - AiContentTemplate interface: promptView() now takes TemplateContext - ImageCardTemplate handles both single and carousel via context.isCarousel; schema() returns the correct shape per mode; assemble() routes to assembleSingle/assembleCarousel - TweetCardTemplate handles both single and carousel; promptView() returns tweet_card_carousel view when isCarousel; schema() returns {tweet_text} or {caption, slides[]}; assemble() calls forTweetCard (single) or forTweetCardCarousel (carousel) - CarouselTemplate deleted — carousel is now a format dimension each style handles, not a standalone style - AiTemplateRegistry: [ImageCardTemplate, TweetCardTemplate] only - PostImagePipeline: adds forTweetCardCarousel — renders one renderTweetCard per slide text, returns N media items - StreamPostCreation: carousel detection restored to $format === CAROUSEL_FORMAT (like main); style dispatched via AiTemplateRegistry::find($this->template); humanize skips tweet_card (single and carousel) - PostContentGenerator: promptView() call updated to pass templateContext - New prompt: resources/views/prompts/post_content/tweet_card_carousel.blade.php - Tests: TemplateContractTest and TweetCardTemplateTest updated for promptView(context) signature; AiTemplateRegistryTest asserts [image_card, tweet_card]; StreamPostCreationTest adds tweet_card carousel test; TweetCardTemplateTest covers both schema shapes and both promptView return values
2026-06-17 21:59:41 +00:00
$workspace = Workspace::factory()->create();
$context = new TemplateContext($workspace, null, 'instagram_feed', 0, false);
expect($t->style())->toBe(ContentStyle::ImageCard)
->and($t->key())->toBe('image_card')
->and($t->needsAccount())->toBeFalse()
->and($t->appliesBrandVisuals())->toBeTrue()
feat(ai-templates): styles orthogonal to single/carousel; tweet-card carousel - TemplateContext gains bool $isCarousel (set from format === CAROUSEL_FORMAT in StreamPostCreation) - AiContentTemplate interface: promptView() now takes TemplateContext - ImageCardTemplate handles both single and carousel via context.isCarousel; schema() returns the correct shape per mode; assemble() routes to assembleSingle/assembleCarousel - TweetCardTemplate handles both single and carousel; promptView() returns tweet_card_carousel view when isCarousel; schema() returns {tweet_text} or {caption, slides[]}; assemble() calls forTweetCard (single) or forTweetCardCarousel (carousel) - CarouselTemplate deleted — carousel is now a format dimension each style handles, not a standalone style - AiTemplateRegistry: [ImageCardTemplate, TweetCardTemplate] only - PostImagePipeline: adds forTweetCardCarousel — renders one renderTweetCard per slide text, returns N media items - StreamPostCreation: carousel detection restored to $format === CAROUSEL_FORMAT (like main); style dispatched via AiTemplateRegistry::find($this->template); humanize skips tweet_card (single and carousel) - PostContentGenerator: promptView() call updated to pass templateContext - New prompt: resources/views/prompts/post_content/tweet_card_carousel.blade.php - Tests: TemplateContractTest and TweetCardTemplateTest updated for promptView(context) signature; AiTemplateRegistryTest asserts [image_card, tweet_card]; StreamPostCreationTest adds tweet_card carousel test; TweetCardTemplateTest covers both schema shapes and both promptView return values
2026-06-17 21:59:41 +00:00
->and($t->promptView($context))->toBe('prompts.post_content.generator')
->and($t->supportedFormats())->toBe([]);
});
test('tweet-card templates do not apply brand visuals', function () {
expect((new TweetCardTemplate)->appliesBrandVisuals())->toBeFalse()
->and((new TweetCardImageTemplate)->appliesBrandVisuals())->toBeFalse();
});
feat(ai-templates): styles orthogonal to single/carousel; tweet-card carousel - TemplateContext gains bool $isCarousel (set from format === CAROUSEL_FORMAT in StreamPostCreation) - AiContentTemplate interface: promptView() now takes TemplateContext - ImageCardTemplate handles both single and carousel via context.isCarousel; schema() returns the correct shape per mode; assemble() routes to assembleSingle/assembleCarousel - TweetCardTemplate handles both single and carousel; promptView() returns tweet_card_carousel view when isCarousel; schema() returns {tweet_text} or {caption, slides[]}; assemble() calls forTweetCard (single) or forTweetCardCarousel (carousel) - CarouselTemplate deleted — carousel is now a format dimension each style handles, not a standalone style - AiTemplateRegistry: [ImageCardTemplate, TweetCardTemplate] only - PostImagePipeline: adds forTweetCardCarousel — renders one renderTweetCard per slide text, returns N media items - StreamPostCreation: carousel detection restored to $format === CAROUSEL_FORMAT (like main); style dispatched via AiTemplateRegistry::find($this->template); humanize skips tweet_card (single and carousel) - PostContentGenerator: promptView() call updated to pass templateContext - New prompt: resources/views/prompts/post_content/tweet_card_carousel.blade.php - Tests: TemplateContractTest and TweetCardTemplateTest updated for promptView(context) signature; AiTemplateRegistryTest asserts [image_card, tweet_card]; StreamPostCreationTest adds tweet_card carousel test; TweetCardTemplateTest covers both schema shapes and both promptView return values
2026-06-17 21:59:41 +00:00
test('image card carousel context returns carousel schema shape', function () {
$t = new ImageCardTemplate;
$workspace = Workspace::factory()->create();
$context = new TemplateContext($workspace, null, 'instagram_carousel', 3, true);
$schema = new JsonSchemaTypeFactory;
$result = $t->schema($schema, $context);
expect($result)->toHaveKeys(['caption', 'slides'])
->and($t->promptView($context))->toBe('prompts.post_content.generator');
});
test('image card single context returns single schema shape', function () {
$t = new ImageCardTemplate;
$workspace = Workspace::factory()->create();
$context = new TemplateContext($workspace, null, 'instagram_feed', 1, false);
$schema = new JsonSchemaTypeFactory;
$result = $t->schema($schema, $context);
expect($result)->toHaveKeys(['content', 'image_title', 'image_body', 'image_keywords']);
});