# Tester qu’une ability WordPress 6.9 respecte son schéma déclaré

> Vérifier automatiquement la conformité d'une ability exposée par une extension avant qu'un agent externe ne l'appelle sans garde-fou en production.

- Auteur : Clément Hadrot
- Publié le : 2025-09-22
- Mis à jour le : 2025-09-22
- Catégorie : Tests
- URL : https://wpmoderne.dev.wordpress-developpement.fr/tests/tester-ability-wordpress-6-9-schema/

## L’essentiel

- Une ability mal validée peut recevoir n'importe quelle entrée d'un agent
- Le schéma JSON déclaré doit être vérifié contre des cas limites réels
- Un test de conformité protège autant l'agent appelant que le site

L'Abilities API, introduite avec WordPress 6.9, expose des capacités structurées qu'un agent externe — assistant IA, automatisation, intégration MCP — peut découvrir et invoquer de façon standardisée, chacune décrite par un schéma d'entrée et de sortie au format JSON Schema. Contrairement à un endpoint REST classique conçu pour un client humain via une interface, une ability est pensée pour être appelée par un système automatisé qui va s'appuyer strictement sur le schéma déclaré pour construire ses appels, sans jamais lire de documentation humaine en marge.

Cette rigueur attendue impose une discipline de test différente de celle d'un endpoint REST ordinaire : il ne suffit pas de vérifier que l'ability fonctionne avec une entrée bien formée, il faut vérifier qu'elle rejette proprement toute entrée qui s'écarte du schéma déclaré, faute de quoi un agent mal informé pourrait provoquer un comportement inattendu en production.

## Déclarer une ability avec un schéma explicite

```
add_action( 'abilities_api_init', function() {
    wp_register_ability( 'mon-plugin/verifier-disponibilite-chambre', [
        'label'               => __( 'Vérifier la disponibilité d’une chambre', 'mon-plugin' ),
        'description'         => __( 'Retourne la disponibilité d’une chambre sur une période donnée.', 'mon-plugin' ),
        'input_schema'        => [
            'type'       => 'object',
            'properties' => [
                'chambre_id' => [ 'type' => 'integer', 'minimum' => 1 ],
                'date_debut' => [ 'type' => 'string', 'format' => 'date' ],
                'date_fin'   => [ 'type' => 'string', 'format' => 'date' ],
            ],
            'required'   => [ 'chambre_id', 'date_debut', 'date_fin' ],
        ],
        'output_schema'       => [
            'type'       => 'object',
            'properties' => [
                'disponible' => [ 'type' => 'boolean' ],
            ],
        ],
        'execute_callback'    => 'mon_plugin_verifier_disponibilite',
        'permission_callback' => fn() => current_user_can( 'manage_reservations' ),
    ] );
} );
```

## Écrire un test qui vérifie la conformité au schéma déclaré

> L'essentiel à retenir : Une ability mal validée peut recevoir n'importe quelle entrée d'un agent ; Le schéma JSON déclaré doit être vérifié contre des cas limites réels ; Un test de conformité protège autant l'agent appelant que le site

```
class Test_Ability_Disponibilite_Chambre extends WP_UnitTestCase {

    public function test_entree_valide_produit_sortie_conforme_au_schema() {
        $ability = wp_get_ability( 'mon-plugin/verifier-disponibilite-chambre' );
        $this->assertNotNull( $ability );

        $resultat = $ability->execute( [
            'chambre_id' => 12,
            'date_debut' => '2025-09-22',
            'date_fin'   => '2025-09-25',
        ] );

        $this->assertIsArray( $resultat );
        $this->assertArrayHasKey( 'disponible', $resultat );
        $this->assertIsBool( $resultat['disponible'] );
    }

    public function test_entree_sans_champ_requis_est_rejetee() {
        $ability = wp_get_ability( 'mon-plugin/verifier-disponibilite-chambre' );

        $resultat = $ability->execute( [
            'chambre_id' => 12,
            // date_fin manquant, le schéma le rend pourtant obligatoire
        ] );

        $this->assertWPError( $resultat );
    }

    public function test_type_incorrect_est_rejete() {
        $ability = wp_get_ability( 'mon-plugin/verifier-disponibilite-chambre' );

        $resultat = $ability->execute( [
            'chambre_id' => 'douze', // chaîne au lieu d’un entier attendu
            'date_debut' => '2025-09-22',
            'date_fin'   => '2025-09-25',
        ] );

        $this->assertWPError( $resultat );
    }
}
```

## Tester les cas limites qu'un agent réel peut envoyer

Un agent externe ne se contente pas de champs manquants ou de types incorrects : il peut aussi envoyer des valeurs syntaxiquement valides mais sémantiquement absurdes, comme une date de fin antérieure à la date de début. Le schéma JSON de base ne capture pas cette contrainte métier, il faut donc la vérifier séparément dans le callback d'exécution, avec un test dédié :

```
public function test_date_fin_anterieure_date_debut_est_rejetee() {
    $ability = wp_get_ability( 'mon-plugin/verifier-disponibilite-chambre' );

    $resultat = $ability->execute( [
        'chambre_id' => 12,
        'date_debut' => '2025-09-25',
        'date_fin'   => '2025-09-22',
    ] );

    $this->assertWPError( $resultat );
    $this->assertSame( 'periode_invalide', $resultat->get_error_code() );
}
```

## Vérifier aussi la permission, pas seulement la structure

- Un agent authentifié sans la capacité `manage_reservations` doit recevoir un refus explicite, jamais une exécution silencieuse.
- Le message d'erreur retourné à l'agent doit rester exploitable par une machine (code d'erreur stable), pas seulement lisible par un humain.
- La sortie doit toujours respecter `output_schema`, y compris dans les cas d'erreur gérés en dehors du mécanisme d'erreur standard.

## En résumé

Ce billet ne traite pas de la conception d'une ability ni du choix de ce qu'elle doit exposer, deux décisions qui relèvent d'un chantier distinct. Il porte uniquement sur la vérification automatisée qu'une ability déjà conçue respecte fidèlement son propre contrat, un contrat sur lequel un agent externe s'appuiera aveuglément puisque c'est précisément la promesse de cette nouvelle API.
