NAME

DBIO::Test::ExtensionSet - Commutativity test for a set of database extension classes (ADR 0066)

VERSION

version 0.900004

SYNOPSIS

In the test of a driver, or of an extension distribution:

use DBIO::Test::ExtensionSet qw(extension_set_ok);

extension_set_ok(
  driver     => 'DBIO::PostgreSQL::Deploy',
  extensions => [
    'DBIO::PostgreSQL::Age::Extension',
    'DBIO::PostgreSQL::PostGIS::Extension',
  ],
  source     => $live_model,
  target     => $target_model,
  dbh        => $fake_dbh,
  changes    => [ { kind => 'column_type', table => ..., column_name => ..., old_info => {...}, new_info => {...} } ],
  name       => 'AGE and PostGIS commute',
);

DESCRIPTION

The protocol of DBIO::Extension::Protocol hands every extension of a phase the same input and merges the answers by unions with a refusal on conflict, so the result does not depend on the order. It cannot ensure that an extension stays inside the protocol: one that changes its input, keeps state between calls, or answers differently depending on what ran before breaks the property (ADR 0066 section 2.4, rule 6 of "WRITING AN EXTENSION" in DBIO::Extension). This module checks exactly that.

Given a driver (a DBIO::Deploy::Base subclass: it applies claims and annotations to the model and may supply the diff class), a set of extension classes and an offline fixture -- a source and a target model, a stand-in for the database handle that the extensions' inspect reads, and optionally changes to ask verdict about -- it runs the phases in every permutation of the set (all permutations up to five extensions; above, every rotation of the name order and of its reverse), overriding the name order only here, and runs each order twice. It asserts

  • the same post-phase source and target model,

  • the same diff statements, the same verdicts and refusals,

  • the same activation and the same inspect states,

  • per extension the same output of every phase in every position and in the repeated run (an extension with state between calls differs from itself),

  • that every extension got a copy of its input and the copy came back unchanged (the input and the context).

An exception from the protocol (an annotate conflict, a malformed answer, a driver that cannot apply what the set claims, a wrong interface version) is a violation as well: a set that cannot run does not commute.

A driver runs it over the extensions of its family; an extension distribution runs it over itself and every extension it names as a peer.

METHODS

extension_set_orders

my @orders = extension_set_orders(@names);

The orders the test runs, each an array reference of names. All permutations for up to five names; above that every rotation of @names and of its reverse (duplicates removed).

extension_set_violations

my @violations = extension_set_violations(%args);

Runs the test and returns the violations as strings; empty when the set commutes. Arguments:

driver

A class name or object of a DBIO::Deploy::Base subclass. A class name is loaded by the test; a class that cannot be loaded throws a DBIO::Exception naming it. The class needs no schema for the hooks the protocol uses (_apply_extension_claims, _apply_extension_annotations, _apply_extension_states, _extension_driver_helpers, _extension_connection_schema, _extension_restricted, _diff_class). It is built with ->new without arguments. Required.

extensions

An array reference of extension classes, or of entries { class, name, options, installed, version, schema }. The name is the class's name unless the entry gives one. An entry without installed state counts as installed; installed (below) overrides.

installed

Optional: name to { installed, version, schema }.

source, target

The offline fixture: the source (live) model and the target model, in the driver's shape. Required. They are copied; the test never changes them.

dbh

What $ctx->{dbh} is in inspect: a hash or fake object the extensions read. Optional.

changes

Optional: array reference of changes to ask verdict about, in the driver's form.

diff

Optional code reference ($source, $target, $protocol) returning the diff statements as an array reference, for a driver whose diff is not a plain _diff_class->new(source =>, target =>, extensions =>)->as_sql. Default: that.

extension_set_ok

extension_set_ok(%args, name => 'the set commutes');

"extension_set_violations" as a test: one ok that fails with the violations as diagnostics. Returns the result of the test. builder may name a Test::Builder to report to (default: the one of Test::More).

AUTHOR

DBIO & DBIx::Class Authors

COPYRIGHT AND LICENSE

Copyright (C) 2026 DBIO Authors Portions Copyright (C) 2005-2025 DBIx::Class Authors Based on DBIx::Class, heavily modified.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.