AmberDB

 view release on metacpan or  search on metacpan

t/amberdb_backup.t  view on Meta::CPAN


    $adb->insert_id( 'orders', undef, 'Alice', 15350 );
    $adb->insert_id( 'orders', undef, 'Bob', 1200 );

    # Run full dump
    my $dump_file = "$tmpdir/backup/test_dump.amberdb";
    my ($outfile, $manifest) = $tools->dump( file => $dump_file );

    ok( defined $outfile, "dump() returned output path" );
    is( $outfile, $dump_file, "dump() created requested archive path" );
    ok( -e $dump_file, "Archive file exists on disk" );
    ok( -s $dump_file > 0, "Archive file is non-empty" );

    # Validate returned in-memory manifest structure
    ok( defined $manifest, "dump() returned manifest hashref" );
    is( $manifest->{format}, 'AmberDB Archive', "Manifest format is 'AmberDB Archive'" );
    is( $manifest->{format_version}, 1, "Format version is 1" );
    is( $manifest->{dbases}->{catalog}, 'schema/catalog.dbase', "Manifest records catalog.dbase" );
    ok( exists $manifest->{tables}->{catalog_product}, "Manifest contains catalog_product table metadata" );
    is( $manifest->{tables}->{catalog_product}->{records}, 3, "Manifest records 3 records for catalog_product" );
};

# =========================================================================
# SUBTEST 3: Archive Package Inspection (.tar Extraction & Index Exclusion)
# =========================================================================
subtest "3. Archive Package Inspection & Integrity" => sub {
    plan tests => 8;

    my $dump_file = "$tmpdir/backup/test_dump.amberdb";
    my $tar = Archive::Tar->new();
    ok( $tar->read($dump_file), "Archive is a valid tar archive" );

    my @files = $tar->list_files();
    ok( grep { $_ eq 'manifest.json' } @files, "Archive contains manifest.json" );
    ok( grep { $_ eq 'schema/catalog.dbase' } @files, "Archive contains schema/catalog.dbase" );
    ok( grep { $_ eq 'schema/catalog_product.table' } @files, "Archive contains schema/catalog_product.table" );
    ok( grep { $_ eq 'table/catalog_product.db' } @files, "Archive contains table/catalog_product.db" );
    ok( grep { $_ eq 'table/catalog_product.unq' } @files, "Archive contains table/catalog_product.unq" );

    # Verify that derived index files are NOT packaged in the archive
    my @inx_files = grep { /\.inx$|\.src$|\.fac$|\.fld$|\.srt$/ } @files;
    is( scalar(@inx_files), 0, "Derived index files (.inx, .src, .fac, .srt) are NOT in archive" );

    # Validate embedded manifest checksum
    my $manifest_content = $tar->get_content('manifest.json');
    my $manifest = JSON::PP::decode_json($manifest_content);
    my $sha = $manifest->{tables}->{catalog_product}->{sha256}->{'table/catalog_product.db'};
    ok( defined $sha && length($sha) == 64, "Manifest contains valid 64-character SHA-256 hash" );
};

# =========================================================================
# SUBTEST 4: Tools->restore() and Deterministic Index Rebuilding
# =========================================================================
subtest "4. Database Restore and Index Reconstruction" => sub {
    plan tests => 17;

    my $dump_file = "$tmpdir/backup/test_dump.amberdb";

    # 4.1 Safety check: Restore into non-empty database without force should fail
    my $tools = AmberDB::Tools->new($adb);
    my $blocked_res = $tools->restore( file => $dump_file, force => 0 );
    ok( !defined $blocked_res, "restore() without force on non-empty database returns undef" );

    # 4.2 Create a completely clean staging database directory
    my $stagedir = tempdir( CLEANUP => 1 );
    $stagedir =~ s{\\}{/}g;

    my $stage_adb = AmberDB->new(
        path => { dbase_dir => $stagedir },
        cfg  => { user => "admin", language => "tr" },
    );
    my $stage_tools = AmberDB::Tools->new($stage_adb);

    # 4.3 Restore archive with automatic reindexing into staging DB
    my $res = $stage_tools->restore( file => $dump_file, reindex => 1 );
    ok( defined $res, "restore() succeeded on clean database directory" );
    is( $res->{ok}, 1, "Result ok is 1" );
    is( $res->{reindexed}, 1, "Reindexing completed successfully" );

    # 4.4 Verify .dbase, .table, and .unq files were restored
    my $stage_schema_dir = $stage_adb->path('schema_dir') || "$stagedir/schema";
    ok( -e "$stage_schema_dir/catalog.dbase", "catalog.dbase restored to schema directory" );
    ok( -e "$stagedir/table/catalog_product.unq", "catalog_product.unq restored to tables directory" );

    my $restored_dbase = $stage_adb->dbase_info('catalog');
    ok( defined $restored_dbase, "catalog dbase_info loaded" );
    is( $restored_dbase->{name}, 'Product Catalog Group', "dbase name matches original" );

    my $restored_schema = $stage_adb->table_info('catalog_product');
    ok( defined $restored_schema, "catalog_product schema restored" );
    is( $restored_schema->{name}, 'catalog_product', "Schema name is 'catalog_product'" );

    # 4.5 Verify record count and read_id
    is( scalar( $stage_adb->table_keys('catalog_product') ), 3, "catalog_product table count is 3" );
    my @rec1 = $stage_adb->read_id( 'catalog_product', 1 );
    is( $rec1[1], 'Laptop Pro', "Record 1 name is 'Laptop Pro'" );
    is( $rec1[2], 'Electronics', "Record 1 category is 'Electronics'" );

    # 4.6 Verify match index (.fld) was reconstructed and works
    my @matched = $stage_adb->field_fetch( 'catalog_product', 2, 'Furniture' );
    is( scalar(@matched), 2, "field_fetch('Furniture') returns 2 records after restore" );

    # 4.7 Verify full-text search index (.src) was reconstructed and works
    my @search_results = $stage_adb->search_table( 'catalog_product', 'Laptop' );
    is( scalar(@search_results), 1, "search_table('Laptop') returns 1 result" );
    is( $search_results[0]->[0], 1, "search_table returned record ID 1" );
    is( $search_results[0]->[1], 'Laptop Pro', "search_table returned matching product name" );
};



( run in 0.988 second using v1.01-cache-2.11-cpan-800906f7e73 )