UNIT TEST to Controller of Amon2 (for perl) by Test::More

Standard

You can unit test to controller of Amon2
If you make Amon2 plug-in “get_dummy_context()” as below,

use strict;
use utf8;
use t::Util;
use CGI;
use Date::Calc;
use JSON;
use Plack::Session;
use Plack::Test;
use Plack::Util;
use Test::More;
use Data::Dumper;

Ean->bootstrap;
use_ok('Ean::Controller::MemberEdit');
use_ok('Ean::ObjModel::Member');

my $SMART_ID = 'mae';
my $CGI;
my $SESSION;
my $REQUEST;


subtest 'confirm' => sub {

    my $class = 'Ean::Controller::MemberEdit';

    my $c = get_dummy_context();
    $c->session->set( navi_id => $SMART_ID );

    $c->req->param( 'navi_email_send', 1 );
    $c->req->param( 'ean_email1',      'ndds-test@example.com' );
    $c->req->param( 'ean_email1_conf', 'ndds-test@example.com' );
    $c->req->param( 'ean_email1_send', 1 );
    my $ret_data = $class->confirm($c);
    #JSONで返す型(int)もcheck
    like($ret_data, qr/"ean_email2_send":1/o);
    like($ret_data, qr/"show_bems_area_1":1/o);
    like($ret_data, qr/"show_bems_area_2":1/o);
    like($ret_data, qr/"navi_email_send":1/o);
    like($ret_data, qr/"ean_email1":"ndds-test\@example.com"/o);
    like($ret_data, qr/"ean_email1_conf":"ndds-test\@example.com"/o);
    like($ret_data, qr/"ean_email1_send":1/o);
    like($ret_data, qr/"ean_email2":"ndds-test2\@example.com"/o);
    like($ret_data, qr/"ean_email2_conf":"ndds-test2\@example.com"/o);

};


sub date_str {
    my ($day_diff) = @_;
    my @date = Date::Calc::Add_Delta_Days(Date::Calc::Today, $day_diff||0);
    return sprintf("%04d-%02d-%02d", @date);
}

sub now_str {
    my ($day_diff) = @_;
    my @datetime =
        Date::Calc::Add_Delta_YMDHMS(Date::Calc::Today_and_Now,
                                     0,0,$day_diff||0, 0,0,0);
    return sprintf("%04d-%02d-%02d %02d:%02d:%02d", @datetime);
}


sub get_dummy_context {
    no strict 'refs';
    no warnings 'redefine';

    *{"Ean\::session"} =
        sub {
            return $SESSION if $SESSION;

            my $dummy_env = {'psgix.session'=>{},
                             'psgix.session.options'=>{}};
            $SESSION = Plack::Session->new( $dummy_env );
            return $SESSION;
        };
    *{"Ean\::req"} =
        sub {
            return $CGI if $CGI;

            $CGI = CGI->new();
            return $CGI;
        };

    *{"Ean\::render_json"} =
        sub {
            my ($self,$perl_obj) = @_;
            return JSON::to_json($perl_obj);
        };

    *{"Ean\::request"} =
        sub {
            return $REQUEST if $REQUEST;

            $REQUEST = DummyRequest->new();
            return $REQUEST;
        };

    my $c = Ean->bootstrap;
    return $c;
}


done_testing;


package DummyRequest;

sub new {
    my ($class) = @_;
    my $self = {};
    $self =  bless $self, $class;
    return $self;
}

sub cookies { return {}; }
sub env { return {}; }

__DATA__

How can JSON for perl distinguish between “string” and “numeric” ?

Standard

JSON.pm distinguish between “string” and “numeric” , and convert from perl object to json string.

#!/usr/local/bin/perl
use strict;
use JSON;

main();
sub main {
    print JSON::encode_json({val=>'10'}),"\n";
    print JSON::encode_json({val=>10}),"\n";
}
1;

↑↓

$ ./test_encode_json.pl 
{"val":"10"}
{"val":10}

Document of JSON describe as belog,

number
A JSON number becomes either an integer, numeric (floating point) or string scalar in perl, depending on its range and any fractional parts. On the Perl level, there is no difference between those as Perl handles all the conversion details, but an integer may take slightly less memory and might represent more values exactly than floating point numbers.

If the number consists of digits only, JSON will try to represent it as an integer value. If that fails, it will try to represent it as a numeric (floating point) value if that is possible without loss of precision. Otherwise it will preserve the number as a string value (in which case you lose roundtripping ability, as the JSON number will be re-encoded to a JSON string).

JSON::PP backend of JSON uses B module

There is source code snippet of JSON::PP.

sub value_to_json {
    my ($self, $value) = @_;

    return 'null' if(!defined $value);

    my $b_obj = B::svref_2object(\$value);  # for round trip problem
    my $flags = $b_obj->FLAGS;
    ######## HERE !!!! ########
    return $value # as is 
        if $flags & ( B::SVp_IOK | B::SVp_NOK ) and !( $flags & B::SVp_POK ); # SvTYPE is IV or NV?

    my $type = ref($value);

    if(!$type){
        return string_to_json($self, $value);
    }
    elsif( blessed($value) and  $value->isa('JSON::PP::Boolean') ){
        return $$value == 1 ? 'true' : 'false';
    }
    elsif ($type) {
        if ((overload::StrVal($value) =~ /=(\w+)/)[0]) {
            return $self->value_to_json("$value");
        }

        if ($type eq 'SCALAR' and defined $$value) {
            return   $$value eq '1' ? 'true'
                   : $$value eq '0' ? 'false'
                   : $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ? 'null'
                   : encode_error("cannot encode reference to scalar");
        }

         if ( $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ) {
             return 'null';
         }
         else {
             if ( $type eq 'SCALAR' or $type eq 'REF' ) {
                encode_error("cannot encode reference to scalar");
             }
             else {
                encode_error("encountered $value, but JSON can only represent references to arrays or hashes");
             }
         }

    }
    else {
        return $self->{fallback}->($value)
             if ($self->{fallback} and ref($self->{fallback}) eq 'CODE');
        return 'null';
    }

}

http://search.cpan.org/perldoc?JSON%3A%3APP

You can distinguish between "string" and "numeric" too, as below.

#!/usr/local/bin/perl
use strict;
use B ();

main();
sub main {

    for my $value (10, "10", 10.0,  10.1, "10"){
        my $b_obj = B::svref_2object(\$value);
        my $flags = $b_obj->FLAGS;
        my $comp_1 = ($flags & ( B::SVp_IOK | B::SVp_NOK )),"\n";
        my $comp_2 =  ( $flags & B::SVp_POK ),"\n";
        if( $comp_1 and not $comp_2){
            print "$value is NUMERIC\n";
        } else {
            print "$value is            STRING\n";
        }
    }
}

“Net::SMTPS for perl” needs google application password, when using gmail smtp.

Standard
#!/usr/local/bin/perl
use strict;
use utf8;
use FindBin;
use File::Spec;
use lib File::Spec->catdir( $FindBin::Bin, '../lib' );
use Encode;
use Net::SMTPS;
use MIME::Base64;
use Data::Dumper;

my $SMTP_CONF = {
    host     => 'smtp.gmail.com',
    port     => '587',
    from     => '?????@gmail.com',
    auth_uid => '?????@gmail.com',

    ### NOTICE!! application password
    ### https://myaccount.google.com/security#signin
    ### https://support.google.com/mail/answer/14257
    auth_pw  => '$APPLICATION_PASSWORD'
};

main();

sub main {
    my $ssl = 'starttls';    # 'ssl' / 'starttls' / undef

    my $smtp = Net::SMTPS->new(
        $SMTP_CONF->{host},
        Port  => $SMTP_CONF->{port},
        doSSL => $ssl,
        Debug => 1
    );

    $smtp->auth( $SMTP_CONF->{auth_uid}, $SMTP_CONF->{auth_pw} )
        or die "can't login smtp server";

    my $mailto = ['ないしょ@gmail.com'];
    my $mailto_str = join( ',', @$mailto );

    my $subject_org = 'これはテストです';
    my $subject = Encode::encode( 'MIME-Header-ISO_2022_JP', $subject_org );

    my $message = <<EOF;
このメールはテストです
EOF

    #メールのヘッダーを構築
    my $header = << "MAILHEADER_1";
From: $SMTP_CONF->{from}
Return-path: $SMTP_CONF->{from}
Reply-To: $SMTP_CONF->{from}
To: $mailto_str
MAILHEADER_1

    $header .= <<"MAILHEADER_2";
Subject: $subject
Mime-Version: 1.0
Content-Type: text/plain; charset = "ISO-2022-JP"
Content-Transfer-Encoding: 7bit
MAILHEADER_2

    $message = encode( 'iso-2022-jp', $message );

    $smtp->mail( $SMTP_CONF->{from} );
    $smtp->to(@$mailto);
    $smtp->data();
    $smtp->datasend("$header\n");
    $smtp->datasend("$message\n");
    $smtp->dataend();
    $smtp->quit;
}

If you set google login password , you have error message below,

$ ./test_send_mail_by_gmail_smtp.pl 
Net::SMTPS>>> Net::SMTPS(0.03)
Net::SMTPS>>>   IO::Socket::INET(1.33)
Net::SMTPS>>>     IO::Socket(1.36)
Net::SMTPS>>>       IO::Handle(1.34)
Net::SMTPS>>>         Exporter(5.68)
Net::SMTPS>>>   Net::SMTP(3.05)
Net::SMTPS>>>     Net::Cmd(3.05)
Net::SMTPS>>>     IO::Socket::IP(0.37)
Net::SMTPS=GLOB(0x2b41ae0)<<< 220 smtp.gmail.com ESMTP i69sm1748185pfk.30 - gsmtp
Net::SMTPS=GLOB(0x2b41ae0)>>> EHLO localhost.localdomain
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-smtp.gmail.com at your service, [61.21.205.219]
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-SIZE 35882577
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-8BITMIME
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-STARTTLS
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-ENHANCEDSTATUSCODES
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-PIPELINING
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-CHUNKING
Net::SMTPS=GLOB(0x2b41ae0)<<< 250 SMTPUTF8
Net::SMTPS=GLOB(0x2b41ae0)>>> STARTTLS
Net::SMTPS=GLOB(0x2b41ae0)<<< 220 2.0.0 Ready to start TLS
Net::SMTPS=GLOB(0x2b41ae0)>>> EHLO localhost.localdomain
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-smtp.gmail.com at your service, [61.21.205.219]
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-SIZE 35882577
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-8BITMIME
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-ENHANCEDSTATUSCODES
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-PIPELINING
Net::SMTPS=GLOB(0x2b41ae0)<<< 250-CHUNKING
Net::SMTPS=GLOB(0x2b41ae0)<<< 250 SMTPUTF8
Net::SMTPS=GLOB(0x2b41ae0)>>> AUTH LOGIN
Net::SMTPS=GLOB(0x2b41ae0)<<< 334 VXNlcm5hbWU6
Net::SMTPS=GLOB(0x2b41ae0)>>> c21hcnQ4MTZwaWthQGdtYWlsLmNvbQ==
Net::SMTPS=GLOB(0x2b41ae0)<<< 334 UGFzc3dvcmQ6
Net::SMTPS=GLOB(0x2b41ae0)>>> 
Net::SMTPS=GLOB(0x2b41ae0)<<< 535-5.7.8 Username and Password not accepted. Learn more at
Net::SMTPS=GLOB(0x2b41ae0)<<< 535 5.7.8  https://support.google.com/mail/answer/14257 i69sm1748185pfk.30 - gsmtp

install free-ssl-cert (Let’s Encrypt) to nginx on amazon linux

Standard

STEP0 refer to

https://letsencrypt.jp/docs/using.html#installation

STEP1 install client

# cd /usr/local
git clone https://github.com/certbot/certbot

STEP2 install ssl cert

# /usr/local/certbot/certbot-auto certonly --debug --webroot \
>   -d hoge.example.mydns.jp \
>   --webroot-path /usr/share/nginx/html
Version: 1.1-20080819
Version: 1.1-20080819

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/hoge.example.mydns.jp/fullchain.pem. Your
   cert will expire on 2016-10-05. To obtain a new or tweaked version
   of this certificate in the future, simply run certbot-auto again.
   To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le


# ls -l /etc/letsencrypt/live/hoge.example.mydns.jp
  cert.pem -> ../../archive/hoge.example.mydns.jp/cert1.pem
  chain.pem -> ../../archive/hoge.example.mydns.jp/chain1.pem
  fullchain.pem -> ../../archive/hoge.example.mydns.jp/fullchain1.pem
  privkey.pem -> ../../archive/hoge.example.mydns.jp/privkey1.pem

STEP3 setting nginx

# vi /etc/nginx/nginx.conf
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    include /etc/nginx/conf.d/*.conf;
    index   index.html index.htm;

    server {
        listen       80;
    return 302 https://$host$request_uri;
    }



  server {
    listen 443 ssl;

    ssl_certificate /etc/letsencrypt/live/hoge.example.mydns.jp/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hoge.example.mydns.jp/privkey.pem;

    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;

    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    root /usr/share/nginx/html;

    index index.html index.htm index.nginx-debian.html;

    server_name _;

    location / {
      # First attempt to serve request as file, then
      # as directory, then fall back to displaying a 404.
      try_files $uri $uri/ =404;
    }
  }
}

STEP4 restart nginx

# /etc/rc.d/init.d/nginx restart

OTHER

SSL Cert on Let’s Encrypt expires 90 days.
You can update cert by command “certbot renew” .