Skip to the content.

← Back to Table of Contents

7. Development Workflow

This section documents the development practices, standards, and workflows used in the Kingdom Management Portal project.

7.1 Coding Standards

KMP follows the CakePHP coding standards with some additional project-specific rules.

PHP Coding Standards

The project uses PHP_CodeSniffer with the CakePHP ruleset to enforce coding standards:

cd /workspaces/KMP/app

# Check coding standards
composer cs-check

# Automatically fix coding standards issues
composer cs-fix

# Run all checks (tests + coding standards)
composer check

# Or run phpcs directly
vendor/bin/phpcs --colors -p
vendor/bin/phpcbf --colors -p

Key coding standards include:

Static Analysis

PHPStan is used for static analysis:

cd /workspaces/KMP/app

# PHPStan static analysis
composer stan

# Or run directly
vendor/bin/phpstan analyse

Documentation Standards

7.2 Testing

KMP uses PHPUnit 10.x with a seed SQL + transaction wrapping strategy — NOT CakePHP fixtures.

Test Data Strategy

Test data comes from dev_seed_clean.sql, loaded once at bootstrap via SeedManager. Each test runs inside a database transaction that rolls back automatically, so tests never affect each other.

How it works:

  1. tests/bootstrap.php calls SeedManager::bootstrap('test') to load dev_seed_clean.sql
  2. BaseTestCase::setUp() opens a transaction
  3. Your test runs against the full seed dataset
  4. BaseTestCase::tearDown() rolls the transaction back

Test Suites

Test suites are defined in phpunit.xml.dist:

Suite Directories Purpose
core-unit tests/TestCase/Core/Unit, tests/TestCase/Model, tests/TestCase/Services, tests/TestCase/KMP, ApplicationTest.php Fast unit/service tests
core-feature tests/TestCase/Core/Feature, tests/TestCase/Controller, tests/TestCase/Command, tests/TestCase/Middleware, tests/TestCase/View HTTP and controller tests
plugins tests/TestCase/Plugins, plugins/*/tests/TestCase Plugin tests
all Everything Complete regression suite

Running Tests

cd /workspaces/KMP/app

# Run all tests
composer test
# or
vendor/bin/phpunit

# Run a specific suite
vendor/bin/phpunit --testsuite core-unit
vendor/bin/phpunit --testsuite core-feature
vendor/bin/phpunit --testsuite plugins
vendor/bin/phpunit --testsuite all

# Run a specific test file
vendor/bin/phpunit tests/TestCase/Controller/MembersControllerTest.php

# Run a specific test method
vendor/bin/phpunit --filter testIndex tests/TestCase/Controller/MembersControllerTest.php

# Run with coverage
vendor/bin/phpunit --coverage-html tmp/coverage

JavaScript Tests

cd /workspaces/KMP/app

# Unit tests (Jest)
npm run test:js

# UI/E2E tests (Playwright)
npm run test:ui

Test Structure

app/tests/
├── bootstrap.php                      # Loads seed SQL, configures test DB
├── TestCase/
│   ├── BaseTestCase.php               # Transaction wrapping + data constants
│   ├── TestAuthenticationHelper.php    # Auth helper trait
│   ├── Support/
│   │   ├── HttpIntegrationTestCase.php    # Base for HTTP/controller tests
│   │   ├── PluginIntegrationTestCase.php  # Base for plugin HTTP tests
│   │   └── SeedManager.php                # Loads dev_seed_clean.sql
│   ├── Controller/                    # Controller tests
│   ├── Model/                         # Table and entity tests
│   ├── Services/                      # Service layer tests
│   ├── Command/                       # CLI command tests
│   ├── Middleware/                     # Middleware tests
│   └── View/                          # Helper and cell tests
├── js/                                # Jest unit tests
└── ui/                                # Playwright E2E tests

Writing Tests

Base Classes

All tests extend one of these base classes (never extend Cake\TestSuite\TestCase directly):

Base Class Use For
App\Test\TestCase\BaseTestCase Unit tests (models, entities, services)
App\Test\TestCase\Support\HttpIntegrationTestCase Controller/HTTP tests
App\Test\TestCase\Support\PluginIntegrationTestCase Plugin controller tests

Controller Test Pattern

<?php
declare(strict_types=1);

namespace App\Test\TestCase\Controller;

use App\Test\TestCase\Support\HttpIntegrationTestCase;

class MembersControllerTest extends HttpIntegrationTestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        $this->enableCsrfToken();
        $this->enableSecurityToken();
        $this->authenticateAsSuperUser();
    }

    public function testIndex(): void
    {
        $this->get('/members');
        $this->assertResponseOk();
        $this->assertResponseContains('Members');
    }

    public function testAddWithValidData(): void
    {
        $data = [
            'email_address' => 'newmember@example.com',
            'sca_name' => 'New Member',
        ];
        $this->post('/members/add', $data);
        $this->assertResponseSuccess();
    }
}

Model Test Pattern

<?php
declare(strict_types=1);

namespace App\Test\TestCase\Model\Table;

use App\Test\TestCase\BaseTestCase;

class MembersTableTest extends BaseTestCase
{
    protected $Members;

    protected function setUp(): void
    {
        parent::setUp();
        $this->Members = $this->getTableLocator()->get('Members');
    }

    public function testGetAdmin(): void
    {
        $admin = $this->Members->get(self::ADMIN_MEMBER_ID);
        $this->assertEquals('admin@amp.ansteorra.org', $admin->email_address);
    }
}

Plugin Test Pattern

<?php
declare(strict_types=1);

namespace App\Test\TestCase\Plugins\Officers;

use App\Test\TestCase\Support\PluginIntegrationTestCase;

class OfficersControllerTest extends PluginIntegrationTestCase
{
    protected const PLUGIN_NAME = 'Officers';

    protected function setUp(): void
    {
        parent::setUp();
        $this->enableCsrfToken();
        $this->enableSecurityToken();
        $this->authenticateAsSuperUser();
    }

    public function testIndex(): void
    {
        $this->get('/officers');
        $this->assertResponseOk();
    }
}

Test Data Constants

BaseTestCase provides constants for stable IDs in the seed data:

Constant Value Description
ADMIN_MEMBER_ID 1 Super user (admin@amp.ansteorra.org)
KINGDOM_BRANCH_ID 2 Kingdom of Ansteorra (root branch)
TEST_MEMBER_AGATHA_ID 2871 Local MoAS test member
TEST_MEMBER_BRYCE_ID 2872 Local Seneschal test member
TEST_MEMBER_DEVON_ID 2874 Regional Armored Marshal test member
TEST_MEMBER_EIRIK_ID 2875 Kingdom Seneschal test member
TEST_BRANCH_LOCAL_ID 14 Shire of Adlersruhe
TEST_BRANCH_STARGATE_ID 39 Barony of Stargate
TEST_BRANCH_CENTRAL_REGION_ID 12 Central Region
TEST_BRANCH_SOUTHERN_REGION_ID 13 Southern Region
ADMIN_ROLE_ID 1 Admin role
SUPER_USER_PERMISSION_ID 1 Is Super User permission

Authentication in Tests

Use TestAuthenticationHelper (included automatically via HttpIntegrationTestCase):

// Authenticate as admin/super user
$this->authenticateAsSuperUser();

// Authenticate as a specific test member
$this->authenticateAsMember(self::TEST_MEMBER_AGATHA_ID);

// Log out
$this->logout();

// Assertions
$this->assertAuthenticated();
$this->assertNotAuthenticated();
$this->assertAuthenticatedAs(self::ADMIN_MEMBER_ID);

Helper Assertions (from BaseTestCase)

$this->assertRecordExists('Members', ['email_address' => 'test@example.com']);
$this->assertRecordNotExists('Members', ['id' => 999]);
$this->assertRecordCount('Members', 5, ['status' => 'verified']);

7.3 Debugging

DebugKit

The CakePHP DebugKit panel is enabled in development environments and provides information about request parameters, SQL queries, environment variables, session/cache data, and rendering timelines.

Logging

Log::debug('Operation details', ['context' => $data]);
Log::error('An error occurred', ['exception' => $exception]);

Log files are in app/logs/: debug.log, error.log, queries.log.

Debug Functions

dd($variable);       // Dump and die
debug($variable);    // Dump and continue

7.4 Git Workflow

KMP uses a feature branch workflow for development.

Branch Structure

Git Commands Reference

git clone https://github.com/Ansteorra/KMP.git
cd KMP

git checkout -b feature/new-feature-name
git add .
git commit -m "Descriptive commit message"
git push -u origin feature/new-feature-name

# Update from upstream (when working in a fork)
./merge_from_upstream.sh

# Reset the development database
./reset_dev_database.sh

Commit Message Guidelines

7.5 API Documentation Generation

KMP publishes API references for PHP services and JavaScript Stimulus controllers.

Toolchain

Regenerating Docs

# From repository root
./generate_api_docs.sh

# Or manually
cd app
composer docs:php
npm run docs:js

Output lives in docs/api/php and docs/api/js.

Previewing Documentation

./serve_docs.sh

# Customize host/port
JEKYLL_HOST=0.0.0.0 JEKYLL_PORT=4100 ./serve_docs.sh

Visit http://127.0.0.1:4000/ to browse the docs.


← Back to Table of Contents