coding-standards
Baseline cross-project coding conventions for naming, readability, immutability, and code-quality review. Use detailed frontend or backend skills for framework-specific patterns.
laravel-tdd
Laravel testing strategies with PHPUnit, Pest, model factories, HTTP tests, Sanctum authentication testing, mocking, and coverage.
Full skill instructions
Test-driven development for Laravel applications using PHPUnit, Pest, Laravel factories, and testing helpers.
// Step 1: RED — Write a failing test
public function test_a_product_can_be_created(): void
{
$product = Product::factory()->create(['name' => 'Test Product']);
$this->assertDatabaseHas('products', ['name' => 'Test Product']);
}
// Step 2: GREEN — Write the migration, model, and factory
// Step 3: REFACTOR — Improve while keeping tests green
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">tests/Feature</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_STORE" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
protected function setUp(): void
{
parent::setUp();
// Call $this->withoutExceptionHandling() only in tests that
// test non-HTTP exceptions; it suppresses assertStatus() etc.
}
// Helper: Authenticate and return user
protected function actingAsUser(): mixed
{
$user = \App\Models\User::factory()->create();
$this->actingAs($user);
return $user;
}
protected function actingAsAdmin(): mixed
{
$admin = \App\Models\User::factory()->admin()->create();
$this->actingAs($admin);
return $admin;
}
}
// database/factories/UserFactory.php
class UserFactory extends Factory
{
protected static ?string $password = null;
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
'role' => 'user',
];
}
public function admin(): static
{
return $this->state(fn (array $attributes) => ['role' => 'admin']);
}
public function unverified(): static
{
return $this->state(fn (array $attributes) => ['email_verified_at' => null]);
}
}
// database/factories/ProductFactory.php
class ProductFactory extends Factory
{
public function definition(): array
{
return [
'name' => fake()->unique()->words(3, true),
'slug' => fn (array $attrs) => Str::slug($attrs['name']),
'description' => fake()->paragraph(),
'price' => fake()->numberBetween(100, 100000),
'stock' => fake()->numberBetween(0, 100),
'is_active' => true,
'user_id' => UserFactory::new(),
];
}
public function outOfStock(): static
{
return $this->state(fn (array $attributes) => ['stock' => 0]);
}
}
$user = User::factory()->create();
$admin = User::factory()->admin()->create();
$product = Product::factory()->create(['user_id' => $user->id]);
$products = Product::factory()->count(10)->create();
$draft = Product::factory()->make(); // Not persisted
// With relationships
$user = User::factory()->has(Product::factory()->count(3))->create();
// Sequences
User::factory()->count(3)->sequence(
['role' => 'admin'], ['role' => 'editor'], ['role' => 'user'],
)->create();
namespace Tests\Unit\Models;
use App\Models\User;
use App\Models\Product;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class UserTest extends TestCase
{
use RefreshDatabase;
public function test_it_hides_sensitive_attributes(): void
{
$user = User::factory()->create();
$this->assertArrayNotHasKey('password', $user->toArray());
}
public function test_admin_scope_returns_only_admins(): void
{
User::factory()->admin()->create();
User::factory()->count(3)->create();
$this->assertCount(1, User::admin()->get());
}
}
class ProductTest extends TestCase
{
use RefreshDatabase;
public function test_active_scope_filters_correctly(): void
{
Product::factory()->count(3)->create(['is_active' => true]);
Product::factory()->count(2)->create(['is_active' => false]);
$this->assertCount(3, Product::active()->get());
}
public function test_it_belongs_to_a_user(): void
{
$user = User::factory()->create();
$product = Product::factory()->create(['user_id' => $user->id]);
$this->assertTrue($product->user->is($user));
}
}
namespace Tests\Feature\Http\Controllers;
use App\Models\Product;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ProductControllerTest extends TestCase
{
use RefreshDatabase;
public function test_guests_are_redirected_to_login(): void
{
$this->get(route('products.create'))->assertRedirect(route('login'));
}
public function test_it_stores_a_new_product(): void
{
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('products.store'), [
'name' => 'New Product',
'description' => 'Description',
'price' => 2999,
'stock' => 10,
]);
$response->assertRedirect(route('products.index'));
$this->assertDatabaseHas('products', [
'name' => 'New Product',
'user_id' => $user->id,
]);
}
public function test_it_validates_required_fields(): void
{
$this->actingAs(User::factory()->create());
$this->post(route('products.store'), [])
->assertSessionHasErrors(['name', 'price']);
}
public function test_users_cannot_modify_others_products(): void
{
$owner = User::factory()->create();
$attacker = User::factory()->create();
$product = Product::factory()->create(['user_id' => $owner->id]);
$this->actingAs($attacker)
->delete(route('products.destroy', $product))
->assertForbidden();
}
}
namespace Tests\Feature\Http\Controllers\Api;
use App\Models\Product;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ProductApiTest extends TestCase
{
use RefreshDatabase;
public function test_unauthenticated_requests_are_rejected(): void
{
$this->getJson('/api/products')->assertUnauthorized();
}
public function test_it_lists_paginated_products(): void
{
$user = User::factory()->create();
Product::factory()->count(5)->create(['user_id' => $user->id]);
$response = $this->actingAs($user)->getJson('/api/products');
$response->assertOk();
$response->assertJsonCount(5, 'data');
$response->assertJsonStructure([
'data' => [['id', 'name', 'price']],
'meta' => ['current_page', 'last_page', 'total'],
]);
}
public function test_it_creates_a_product(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->postJson('/api/products', [
'name' => 'API Product',
'price' => 4999,
]);
$response->assertCreated();
$response->assertJsonPath('data.name', 'API Product');
}
public function test_users_cannot_delete_others_products(): void
{
$owner = User::factory()->create();
$attacker = User::factory()->create();
$product = Product::factory()->create(['user_id' => $owner->id]);
$this->actingAs($attacker)
->deleteJson("/api/products/{$product->id}")
->assertForbidden();
}
}
namespace Tests\Feature\Http\Controllers\Api;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Tests\TestCase;
class AuthControllerTest extends TestCase
{
use RefreshDatabase;
public function test_users_can_register(): void
{
$response = $this->postJson('/api/register', [
'name' => 'Test User',
'email' => '[email protected]',
'password' => 'Password123!',
'password_confirmation' => 'Password123!',
]);
$response->assertCreated();
$response->assertJsonStructure(['data' => ['user', 'token']]);
}
public function test_users_can_login(): void
{
User::factory()->create([
'email' => '[email protected]',
'password' => Hash::make('Password123!'),
]);
$response = $this->postJson('/api/login', [
'email' => '[email protected]',
'password' => 'Password123!',
]);
$response->assertOk();
$response->assertJsonStructure(['data' => ['token']]);
}
public function test_users_cannot_login_with_wrong_password(): void
{
User::factory()->create(['email' => '[email protected]']);
$this->postJson('/api/login', [
'email' => '[email protected]',
'password' => 'wrong',
])->assertUnprocessable();
}
public function test_token_bearer_authenticates_requests(): void
{
$user = User::factory()->create();
$token = $user->createToken('test')->plainTextToken;
$this->withToken($token)
->getJson('/api/user')
->assertOk()
->assertJsonPath('data.email', $user->email);
}
}
use Illuminate\Support\Facades\Http;
public function test_it_handles_successful_payment(): void
{
Http::fake([
'api.stripe.com/*' => Http::response(['id' => 'pi_123', 'status' => 'succeeded'], 200),
]);
$result = (new PaymentService())->charge(2999);
$this->assertTrue($result->success);
}
public function test_it_handles_gateway_failure(): void
{
Http::fake([
'api.stripe.com/*' => Http::response(['error' => 'card_declined'], 402),
]);
$this->expectException(PaymentFailedException::class);
(new PaymentService())->charge(2999);
}
public function test_it_retries_on_timeout(): void
{
Http::fake([
'api.stripe.com/*' => Http::sequence()
->pushStatus(408)
->pushStatus(200),
]);
$this->assertTrue((new PaymentService())->charge(2999)->success);
}
Mail::fake();
$order->sendConfirmation();
Mail::assertSent(OrderConfirmation::class, function ($mail) use ($order) {
return $mail->hasTo($order->user->email);
});
Notification::fake();
$user->notify(new WelcomeUser());
Notification::assertSentTo($user, WelcomeUser::class);
Queue::fake();
ProcessImage::dispatch($product);
Queue::assertPushed(ProcessImage::class, function ($job) use ($product) {
return $job->product->id === $product->id;
});
Storage::fake('public');
$file = UploadedFile::fake()->image('photo.jpg', 200, 200);
$response = $this->actingAs($user)->post('/avatar', [
'avatar' => $file,
]);
$response->assertSessionHasNoErrors();
Storage::disk('public')->assertExists('avatars/' . $file->hashName());
Event::fake();
$order->markAsShipped();
Event::assertDispatched(OrderShipped::class, function ($event) use ($order) {
return $event->order->id === $order->id;
});
public function test_it_sends_newsletters(): void
{
Mail::fake();
User::factory()->count(5)->create(['subscribed' => true]);
$this->artisan('newsletter:send')
->expectsOutput('Sending newsletter to 5 subscribers...')
->assertExitCode(0);
Mail::assertSent(NewsletterMail::class, 5);
}
public function test_it_handles_no_subscribers(): void
{
$this->artisan('newsletter:send')
->expectsOutput('No subscribers found.')
->assertExitCode(0);
}
public function test_users_can_update_own_posts(): void
{
$user = User::factory()->create();
$post = Post::factory()->create(['user_id' => $user->id]);
$this->actingAs($user)
->put(route('posts.update', $post), ['title' => 'Updated'])
->assertRedirect();
}
public function test_users_cannot_update_others_posts(): void
{
$post = Post::factory()->create();
$this->actingAs(User::factory()->create())
->put(route('posts.update', $post), ['title' => 'Hacked'])
->assertForbidden();
}
public function test_gate_before_grants_super_admin_full_access(): void
{
$super = User::factory()->create(['role' => 'super-admin']);
$post = Post::factory()->create();
$this->actingAs($super)
->delete(route('posts.destroy', $post))
->assertRedirect();
$this->assertSoftDeleted($post);
}
<?php
use App\Models\Product;
use App\Models\User;
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
$this->user = User::factory()->create();
$this->actingAs($this->user);
});
it('lists products', function () {
Product::factory()->count(3)->create(['user_id' => $this->user->id]);
$this->get(route('products.index'))
->assertOk()
->assertViewHas('products');
});
it('creates a product with valid data', function () {
$this->post(route('products.store'), [
'name' => 'Test Product', 'price' => 1999,
])->assertRedirect();
$this->assertDatabaseHas('products', ['name' => 'Test Product']);
});
it('fails validation without required fields', function () {
$this->post(route('products.store'), [])
->assertSessionHasErrors(['name', 'price']);
});
it('authorizes updates', function () {
$other = User::factory()->create();
$product = Product::factory()->create(['user_id' => $other->id]);
$this->put(route('products.update', $product), ['name' => 'Hacked'])
->assertForbidden();
});
# PHPUnit (use clover output for CI threshold checks)
vendor/bin/phpunit --coverage-html coverage --coverage-clover clover.xml
# Pest (built-in threshold support)
vendor/bin/pest --coverage --min=80
| Component | Target |
|---|---|
| Models | 95%+ |
| Actions/Services | 90%+ |
| Form Requests | 90%+ |
| Controllers | 85%+ |
| Policies | 95%+ |
| Overall | 80%+ |
create() callstest_guests_cannot_create_productsHttp::fake(), Mail::fake()RefreshDatabase for clean state| Pattern | Usage |
|---|---|
RefreshDatabase | Reset database between tests |
$this->actingAs($user) | Authenticate as user |
$this->withToken($token) | Bearer token auth for APIs |
Model::factory()->create() | Create model with factory |
Model::factory()->count(5)->create() | Create multiple records |
Http::fake([...]) | Mock HTTP calls |
Mail::fake() | Trap sent mail |
Notification::fake() | Trap sent notifications |
Queue::fake() | Trap queued jobs |
Event::fake() | Trap dispatched events |
Storage::fake('public') | Trap file operations |
assertDatabaseHas | Assert DB row exists |
assertSoftDeleted | Assert soft-delete |
assertSessionHasErrors | Assert validation errors |
assertForbidden | Assert 403 status |
laravel-patterns — Laravel architecture, Eloquent, routing, and API patternslaravel-security — Laravel authentication, authorization, and secure codingtdd-workflow — The repo-wide RED -> GREEN -> REFACTOR loopbackend-patterns — General backend API and database patternsBaseline cross-project coding conventions for naming, readability, immutability, and code-quality review. Use detailed frontend or backend skills for framework-specific patterns.
Go testing patterns including table-driven tests, subtests, benchmarks, fuzzing, and test coverage. Follows TDD methodology with idiomatic Go practices.
Docker and Docker Compose patterns for local development, container security, networking, volume strategies, and multi-service orchestration.
Spring Boot architecture patterns, REST API design, layered services, data access, caching, async processing, and logging. Use for Java Spring Boot backend work.
AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.
Pythonic idioms, PEP 8 standards, type hints, and best practices for building robust, efficient, and maintainable Python applications.
Create stunning, animation-rich HTML presentations from scratch or by converting PowerPoint files. Use when the user wants to build a presentation, convert a PPT/PPTX to web, or create slides for a talk/pitch. Helps non-designers discover their aesthetic through visual exploration rather than abstract choices.
REST API design patterns including resource naming, status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.
Django architecture patterns, REST API design with DRF, ORM best practices, caching, signals, middleware, and production-grade Django apps.
PostgreSQL database patterns for query optimization, schema design, indexing, and security. Based on Supabase best practices.
Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.
Analyze raw prompts, identify intent and gaps, match ECC components (skills/commands/agents/hooks), and output a ready-to-paste optimized prompt. Advisory role only — never executes the task itself. TRIGGER when: user says "optimize prompt", "improve my prompt", "how to write a prompt for", "help me prompt", "rewrite this prompt", or explicitly asks to enhance prompt quality. Also triggers on Chinese equivalents: "优化prompt", "改进prompt", "怎么写prompt", "帮我优化这个指令". DO NOT TRIGGER when: user wants the task executed directly, or says "just do it" / "直接做". DO NOT TRIGGER when user says "优化代码", "优化性能", "optimize performance", "optimize this code" — those are refactoring/performance tasks, not prompt optimization.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience