Data-Hash-Diff-Smart
view release on metacpan or search on metacpan
t/function.t view on Meta::CPAN
subtest 'compare option: comparator exception is captured' => sub {
my $r = diff(
{x => 1},
{x => 2},
compare => { '/x' => sub { die "boom\n" } },
);
is($r->[0]{op}, 'change', 'comparator exception still records a change');
like($r->[0]{error}, qr/boom/, 'error field contains exception message');
};
subtest 'cycle detection: does not loop infinitely' => sub {
my $a = {x => 1};
$a->{self} = $a;
my $b = {x => 1};
$b->{self} = $b;
my $r;
lives_ok(sub { $r = diff($a, $b) }, 'cycle detection: no infinite loop');
isa_ok($r, 'ARRAY', 'result is still an arrayref after cycle');
};
};
# ===========================================================================
# SECTION 2: diff_text()
# ===========================================================================
subtest 'diff_text()' => sub {
subtest 'returns a string' => sub {
my $t = diff_text({a => 1}, {a => 2});
ok(defined $t, 'returns defined value');
ok(!ref($t), 'returns a plain string');
like($t, qr/\S/, 'non-empty for changed structures');
};
subtest 'no changes produces empty or whitespace-only output' => sub {
my $t = diff_text({a => 1}, {a => 1});
ok(defined $t, 'defined for identical structures');
# Either empty string or whitespace is acceptable
ok(!length($t) || $t =~ /^\s*$/, 'empty/whitespace for no changes');
};
subtest 'output mentions changed path' => sub {
my $t = diff_text({user => {name => 'Alice'}}, {user => {name => 'Bob'}});
like($t, qr/name|user/i, 'output references the changed field');
};
};
# ===========================================================================
# SECTION 3: diff_json()
# ===========================================================================
subtest 'diff_json()' => sub {
subtest 'returns valid JSON string' => sub {
require JSON::MaybeXS;
my $j = diff_json({a => 1}, {a => 2});
ok(defined $j, 'returns a defined value');
my $decoded = eval { JSON::MaybeXS::decode_json($j) };
ok(!$@, 'output is valid JSON');
isa_ok($decoded, 'ARRAY', 'decoded JSON is an array');
};
subtest 'JSON contains op field' => sub {
require JSON::MaybeXS;
my $j = diff_json({a => 1}, {a => 2});
my $decoded = JSON::MaybeXS::decode_json($j);
is($decoded->[0]{op}, 'change', 'first entry has op=change');
};
subtest 'JSON for identical structures' => sub {
require JSON::MaybeXS;
my $j = diff_json({a => 1}, {a => 1});
my $decoded = JSON::MaybeXS::decode_json($j);
is_deeply($decoded, [], 'empty JSON array for no changes');
};
};
# ===========================================================================
# SECTION 4: diff_yaml()
# ===========================================================================
subtest 'diff_yaml()' => sub {
subtest 'returns a YAML string' => sub {
my $y = diff_yaml({a => 1}, {a => 2});
ok(defined $y, 'returns a defined value');
ok(!ref($y), 'returns a plain string');
like($y, qr/\S/, 'non-empty for changed structures');
};
subtest 'YAML contains op key' => sub {
my $y = diff_yaml({a => 1}, {a => 2});
like($y, qr/op.*change|change.*op/s, 'YAML output mentions op: change');
};
subtest 'YAML for identical structures' => sub {
my $y = diff_yaml({a => 1}, {a => 1});
# An empty sequence in YAML is '--- []\n' or '--- \n- \n' etc.
unlike($y, qr/op:/, 'no op entry in YAML for no changes');
};
};
# ===========================================================================
# SECTION 5: diff_test2()
# ===========================================================================
subtest 'diff_test2()' => sub {
subtest 'returns a string for changed structures' => sub {
my $t = diff_test2({a => 1}, {a => 2});
ok(defined $t, 'returns defined value');
ok(!ref($t), 'returns a string');
like($t, qr/\S/, 'non-empty for changes');
};
subtest 'returns something defined for identical structures' => sub {
my $t = diff_test2({a => 1}, {a => 1});
ok(defined $t, 'returns defined for identical structures');
};
};
# ===========================================================================
# SECTION 5b: Renderer::Test2 - white-box unit tests
#
# We call the renderer directly with hand-crafted change lists so each
# branch of render() is exercised in isolation, independent of the diff
# engine.
# ===========================================================================
BEGIN { require Data::Hash::Diff::Smart::Renderer::Test2 }
( run in 1.165 second using v1.01-cache-2.11-cpan-df04353d9ac )