The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl
use strict;
use warnings;

use Test::More;
use Test::Exception;
use Hashids;

plan tests => 5;

my $salt = "this is my salt";

subtest 'basics' => sub {
    plan tests => 8;

    can_ok( Hashids => "new" );
    my $hashids = Hashids->new();
    isa_ok( $hashids, 'Hashids' );

    is( $hashids->salt, '', 'no salt' );

    $hashids = Hashids->new( salt => $salt );
    is( $hashids->salt, $salt, 'low salt' );

    $hashids->new($salt);
    is( $hashids->salt, $salt, 'single-arg constructor' );

    subtest 'hash length' => sub {
        plan tests => 3;

        is( $hashids->minHashLength, 0, 'default minHashLength' );

        my $minHashLength = 8;
        $hashids = Hashids->new( minHashLength => $minHashLength );
        is( $hashids->minHashLength, $minHashLength, 'set minHashLength' );

        $minHashLength = 'top lol';
        throws_ok {
            Hashids->new( minHashLength => $minHashLength );
        }
        qr/not a number/, 'invalid minHashLength';
    };

    subtest 'alphabet' => sub {
        plan tests => 5;

        is( $hashids->alphabet,
            'xcS4F6h89aUbideAI7tkynuopqrXCgTE5GBKHLMjfRsz',
            'default alphabet'
        );

        my $alphabet = join '' => ( 'a' .. 'z' );
        $hashids = Hashids->new( alphabet => $alphabet );
        is( $hashids->alphabet, $alphabet, 'custom alphabet' );

        $alphabet = "abc";
        throws_ok {
            Hashids->new( alphabet => $alphabet );
        }
        qr/must contain at least 4/, 'at least 4 chars';

        $alphabet = "abca";
        throws_ok {
            Hashids->new( alphabet => $alphabet );
        }
        qr/must contain unique/, 'must have unique chars';

        $alphabet = "ab cd";
        throws_ok {
            Hashids->new( alphabet => $alphabet );
        }
        qr/must not have spaces/, 'no spaces allowed';

    };

    subtest 'chars, seps, and guards' => sub {
        plan tests => 3;

        ok( $hashids->chars,  'has chars' );
        ok( $hashids->seps,   'has seps' );
        ok( $hashids->guards, 'has guards' );
    };
};

subtest 'internal functions' => sub {
    plan tests => 4;

    is( Hashids->_consistentShuffle( '123', 'salt' ), '231', 'shuffle 1' );
    is( Hashids->_consistentShuffle( 'abcdefghij', 'salt' ),
        'aichgfdebj', 'shuffle 2' );

    is( Hashids->_hash( 123, 'abcdefghij' ), 'bcd', 'internal hash' );
    is( Hashids->_unhash( 'bcd', 'abcdefghij' ), 123, 'internal unhash' );
};

subtest 'simple encrypt/decrypt' => sub {
    plan tests => 6;

    my $hashids = Hashids->new( salt => $salt );

    is( $hashids->encrypt(),               '', 'no encrypt' );
    is( $hashids->encrypt('up the wazoo'), '', 'bad encrypt' );

    my $plaintext = 123;
    my $encrypted = 'a79';
    is( $hashids->encrypt($plaintext), $encrypted, 'encrypt 1' );
    is( $hashids->decrypt($encrypted), $plaintext, 'decrypt 1' );

    $plaintext = 123456;
    $encrypted = 'AMyLz';
    is( $hashids->encrypt($plaintext), $encrypted, 'encrypt 2' );
    is( $hashids->decrypt($encrypted), $plaintext, 'decrypt 2' );
};

subtest 'list encrypt/decrypt' => sub {
    plan tests => 6;

    my $hashids = Hashids->new( salt => $salt );

    can_ok( $hashids, qw/encrypt decrypt/ );

    my @plaintexts = ( 1, 2, 3 );
    my $encrypted = 'eGtrS8';
    is( $hashids->encrypt(@plaintexts), $encrypted, 'encrypt list 1' );
    is_deeply( scalar $hashids->decrypt($encrypted),
        \@plaintexts, 'decrypt list 1' );

    @plaintexts = ( 123, 456, 789 );
    $encrypted = 'yn8t46hen';
    is( $hashids->encrypt(@plaintexts), $encrypted, 'encrypt list 2' );
    is_deeply( scalar $hashids->decrypt($encrypted),
        \@plaintexts, 'decrypt list 2' );

    subtest 'decrypted return as list' => sub {
        plan tests => 2;

        my @single = $hashids->decrypt('a79');
        is_deeply( \@single, [123], 'decrypted as list (single value)' );

        my @result = $hashids->decrypt($encrypted);
        is_deeply( \@result, \@plaintexts, 'decrypted as list (multi)' );
    };
};

subtest 'work with counting numbers only' => sub {
    my $hashids = Hashids->new();

    plan tests => 4;

    is( $hashids->encrypt(12.3), '', 'not an integer' );
    is( $hashids->encrypt(-1),   '', 'not a positive integer' );
    is( $hashids->encrypt( 123, 45.6 ), '', 'no integer in list' );
    is( $hashids->encrypt( -1, -2, 3 ), '', 'negative integers in list' );
};