Server.php

<?php

namespace Phad\Test\Main;

class Server extends \Phad\Tester {


    public function testUploadFileFail(){
        $documents = $this->post('/document/make/',
            ['title'=>'PD Use of Force Report'],
            ['doc'=>$this->file('test/Server/for-upload/use-of-force-report.txt')]
        );
        
        // echo $documents;
        $clean = trim( substr($documents, 0,strpos($documents, '(')) );

        $this->compare(
            'PD Use of Force Report:use-of-force-report.txt#275',
            $clean,
        );
        // echo $clean;
    }

    /**
     * @test failing a submission when required properties are missing
     * @test partially filled form is delivered when prop requirements are not met
     */
    public function testFailedSubmissionRequiredProperties(){
        $list = $this->get('/blog-names/');
        $create = $this->post('/blog/required-props/', ['title'=>'This should fail', 'body'=>'']);

        $list_after = $this->get('/blog-names/');

        $this->compare($list, $list_after);

        echo $create;
        $this->str_contains($create,
            ['<input type="text" name="title" maxlength="75" required value="This should fail">',
             '<textarea name="body" maxlength="2000" minlength="10" required></textarea>',
            ]     
        );
    }

    /**
     * @test failing a submission bc access is not gratned
     * @test that failed submission response has blank output
     */
    public function testFailedAccessForSubmission(){
        $list = $this->get('/blog-names/');
        $create = $this->post('/blog/make/?deny_access=true', ['title'=>'This should fail', 'body'=>'']);

        $list_after = $this->get('/blog-names/');

        $this->compare($list, $list_after);

        echo $create;

        $empty_form = $this->get('/blog/make/');
        $target = str_replace('value=""', 'value="This should fail"', $empty_form);
        $this->compare($target,$create);
    }

    /**
     * @test being able to create an item, but NOT delete an item
     */
    public function testCreateNoDelete(){
        $this->bootstrap();
        $form = $this->get('/create-no-delete/?phad_action=delete&id=1');

        echo $form;
        $this->str_contains($form, "<p>Deleting 'Candelete' rejected by 'call:never_allow'</p>");

        $create = $this->post('/create-no-delete/',
            $post=[
                // 'id'=>10,
                'title'=>'Created under pressure',
            ]
        );

        echo $create;

        $this->str_contains($create,
            [
                '1-I like bears a lot',
                '4-Created under pressure,'
            ]
        );

    }

    /**
     * @test deletion runs custom access handlers
     */
    public function testRejectDeleteAccessWithCustomHandler(){
        $this->bootstrap();
        $form = $this->get('/delete-not-allowed/?phad_action=delete&id=1');

        echo $form;

        $this->str_contains($form, "<p>Deleting 'Candelete' rejected by 'call:never_allow'</p>");
        $this->str_not_contains($form, '</form><form');

    }
    /**
     * @test forms only enable deletion if `candelete` attribute is set.
     */
    public function testFailToDeleteNonCandelete(){
        $form = $this->get('/blog/make/?phad_action=delete&id=1');

        echo $form;

        $parts = explode('<form',$form);
        $this->is_true(count($parts)==2);
        $this->str_contains(
            $form,
            'value="I like bears a lot"',
            '>Like seriously, I really love bears. They\'re so cute.</textarea>',
        );
    }

    /**
     *
     * @test that a custom function successfully handles a deletion response
     * @test that trying to delete a non-existent item still calls the custom handler
     */
    public function testDeleteCustomResponse(){
        // $this->bootstrap();

        $delete_fail = $this->get('/candelete-custom/?phad_action=delete&id=20');

        $this->compare("This is my custom deletion response. I don't care if deletion succeeded. Id was 20", $delete_fail);

    }

    public function testDeleteFailNotFound(){
        // $this->bootstrap();

        $delete_fail = $this->get('/candelete/?phad_action=delete&id=20');

        $this->compare("Cannot delete 'Candelete'. None exist with id '20'", $delete_fail);

    }

    public function testDeletePrint(){
        $this->bootstrap();
        // $this->benchStart();

        $delete = $this->get('/candelete-print/?phad_action=delete&id=2');

        $this->compare('WooHoo! Delete!', $delete);
    }


    public function testDeleteRedirect(){
        $this->bootstrap();

        $delete = $this->get('/candelete-redirect/?phad_action=delete&id=2');

        $this->compare('index page', $delete);
    }

    /**
     * @test deleting an item via request
     * @test 'success' response for deletion
     */
    public function testDeleteResponse(){
        $this->bootstrap();

        $delete = $this->get('/candelete/?phad_action=delete&id=2');
    

        $this->test('deletion response');
        $this->compare("Successfully deleted Candelete with id '2'", $delete);

        $this->test('deletion succeeded');
        $posts = $this->get('/candelete-list/');

        // echo $posts;

        $this->compare(
            '1-I like bears a lot,3-Baby Fires,',
            $posts
        );
    }

    public function testDeleteItemS(){
        $this->bootstrap();
        $delete = $this->get('/candelete/?phad_action=delete&id=2');

        echo $delete;

        $posts = $this->get('/candelete-list/');

        // echo $posts;

        $this->compare(
            '1-I like bears a lot,3-Baby Fires,',
            $posts
        );

    }

    /**
     * @test custom hasRowAccess() implementation
     */
    public function testCustomRowAccess(){

        $blogs_json = $this->get('/blog-list/?custom_access=true&permit_me=true&get=json');
        // var_dump($blogs_json);
        // exit;
        $blogs = json_decode($blogs_json, true);
        // print_r($blogs);
        // echo $blogs_json;
        // exit;
        $this->is_true(count($blogs)===3);

        // $target = $blogs['BlogList'][0];
        $target = $blogs[0];

        $blogs_json = $this->get(
            '/blog-list/'
            .'?custom_access=true&permit_me=true&get=json'
            .'&title='.urlencode('I like bears a lot')
        );

        // echo $blogs_json;

        $this->compare([$target],
            json_decode($blogs_json,true)
        );
    }

    public function testApproveCustomAccess(){
        $access = $this->get('/access2/?custom_access=true&permit_me=true&user=admin');

        echo $access;
        $this->str_contains($access, '<!-- access-granted -->');
        $this->str_not_contains($access, 'Blogs Not Found');
        $this->str_contains($access, 'Access Granted');
        $this->str_contains($access, '<h1>Before Item Node</h1>');
        $this->str_contains($access, '<h2>After Item Node</h2>');
    }
    
    /**
     * @test custom access handlers
     */
    public function testDenyCustomAccess(){
        // ?permit_me=true is just a really simple way to test this
        $access = $this->get('/access2/?custom_access=true&permit_me=false&user=admin');

        echo $access;
        $this->str_not_contains($access, '<!-- access-granted -->');
        $this->str_not_contains($access, 'Blogs Not Found');
        $this->str_contains($access, '<h1>Before Item Node</h1>');
        $this->str_contains($access, '<h2>After Item Node</h2>');
        // this 403 error displays bc of the `permit_me` access handler returning false, even though the role passes
        $this->str_contains($access, 'User does not have admin role. User has role admin');
    }

    public function testDenyRoleAccess(){
        $access = $this->get('/access/?user=guest');

        echo $access;
        $this->str_not_contains($access, '<!-- access-granted -->');
        $this->str_not_contains($access, 'Blogs Not Found');
        $this->str_contains($access, '<h1>Before Item Node</h1>');
        $this->str_contains($access, '<h2>After Item Node</h2>');
        $this->str_contains($access, 'User does not have admin role. User has role guest');
    }

    public function testApproveRoleAccess(){
        $access = $this->get('/access/?user=admin');

        echo $access;
        $this->str_contains($access, '<!-- access-granted -->');
        $this->str_not_contains($access, 'Blogs Not Found');
        $this->str_contains($access, 'Access Granted');
        $this->str_contains($access, '<h1>Before Item Node</h1>');
        $this->str_contains($access, '<h2>After Item Node</h2>');
    }

    public function testUploadFile(){
        $documents = $this->post('/document/make/',
            ['title'=>'PD Use of Force Report'],
            ['doc'=>$this->file('test/Server/for-upload/use-of-force-report.txt')]
        );
        
        // echo $documents;
        $clean = trim( substr($documents, 0,strpos($documents, '(')) );

        $this->compare(
            'PD Use of Force Report:use-of-force-report.txt#275',
            $clean,
        );
        // echo $clean;
    }
    
    /**
     * @test editing an existing item by passing `id` via GET
     */
    public function testEditBlogForm(){
        $form = $this->get('/blog/make/?id=1');

        echo $form;

        $parts = explode('<form',$form);
        $this->is_true(count($parts)==2);
        $this->str_contains(
            $form,
            'value="I like bears a lot"',
            '>Like seriously, I really love bears. They\'re so cute.</textarea>',
        );

        $this->str_not_contains(
            $form,
            '</form><form',
        );
    }

    /**
     * @test redirecting to target after post submission
     */
    public function testBlogFormRedirect(){

        $post_output = $this->post('/blog/make/',
            $post=[
                'title'=>'The New Post',
                'body'=> 'Hopefully this submits to db without any issue.',
            ]
        );

        echo $post_output;
        // assert only 1 <h1>
        $parts = explode('<h1>',$post_output);
        $this->is_true(count($parts)==2);

        $this->str_contains(
            $post_output,
            '<h1>The New Post</h1>',
            '<p>'.$post['body'].'</p>',
        );
    }


    /** @test inserting data via POST */
    public function testSubmitBlogForm(){
        $names_before = $this->get('/blog-names/');

        $post_output = $this->post('/blog/make/',
            $post=['title'=>'A New Post',
             'body'=> 'Hopefully this submits to db without any issue.',
            ]
        );
        echo $post_output;

        $names_after = $this->get('/blog-names/');
        $this->compare('A New Post,'.$names_before,
            $names_after
        );
    }


    public function testGetBlogForm(){
        $form = $this->get('/blog/make/');

        echo $form;
        $parts = explode('<form',$form);
        $this->is_true(count($parts)==2);
        $this->str_contains(
            $form,
            '<input ',
            '<textarea',
            '></textarea>',
        );
    }

    public function testNestedViews(){
        $out = $this->get('/nested/');
        $json = $this->get('/nested/?get=json');

        echo $out;
        print_r(json_decode($json,true));

        $this->str_contains($out,
            '<h2>Bear</h2>',
                '<b>Reed</b>',
                '<b>Jalin</b>',
            '<h2>Goose</h2>',
                '<b>PooPooMaShu</b>',
                '<b>Baroney</b>',
        );

        // echo $json;
        // exit;

        //@todo re-enable nested items in getting array of data
        $target = '[{"id":"1","name":"Bear"},{"id":"2","name":"Goose"}]';
        // $target = '{"SpeciesList":[{"id":"1","name":"Bear","CharacterList":[{"id":"1","name":"Reed","level":"10","species":"1"},{"id":"2","name":"Jalin","level":"10","species":"1"}]},{"id":"2","name":"Goose","CharacterList":[{"id":"3","name":"PooPooMaShu","level":"10","species":"2"},{"id":"4","name":"Baroney","level":"10","species":"2"}]}]}';
        $this->compare_json($target, $json);

    }
    public function testQueryCharacterJson(){
        $dirty = $this->get('/characters/?get=json');
        $pretty = $this->get('/characters/?get=json&prettyprint=true');

        $expect = '[{"id":"1","name":"Reed","level":"10","species":"Bear"},{"id":"2","name":"Jalin","level":"10","species":"Bear"},{"id":"3","name":"PooPooMaShu","level":"10","species":"Goose"},{"id":"4","name":"Baroney","level":"10","species":"Goose"}]';

        echo $pretty;

        $this->compare_json($expect, $dirty);
        $this->compare_json($expect, $pretty);
        $this->str_contains($pretty,"\n");
        $this->str_not_contains($dirty,"\n");

    }

    public function testSimpleSpeciesQuery(){
        $out = $this->get('/species/');
        echo $out;

        $this->str_contains($out,
            '<span>Bear</span>#<span>1</span>',
            '<span>Goose</span>#<span>2</span>',
        );
    }

    public function testQueryCharacterView(){
        $out = $this->get('/characters/');

        echo $out;
        $this->str_contains($out,
            [
                '<h2>Reed</h2>',
                '<h2>Jalin</h2>',
                '<h2>PooPooMaShu</h2>',
                '<h2>Baroney</h2>',
                '<p>Species: Bear</p>',
                '<p>Species: Goose</p>',
            ]
        );
    }

    public function testServeSitemap(){
        $out = $this->get('/sitemap.xml');

        echo $out;
        $target = 
        <<<XML
        <?xml version="1.0" encoding="UTF-8"?>
        <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
            <url>
                <changefreq>daily</changefreq>
                <lastmod>1354</lastmod>
                <loc>/sitemapped/</loc>
                <priority>0.8</priority>
            </url>
            <url>
                <changefreq>weekly</changefreq>
                <lastmod>123</lastmod>
                <loc>/sitemapped2/</loc>
                <priority>0.5</priority>
            </url>
        </urlset>
        XML;

        $this->compare($target, $out);
    }


    public function testGetDynamicRoute(){
        $out = $this->get('/phad-routed/i-like-bears/');

        echo $out;

        $this->str_contains($out, 'Param is i-like-bears');
        $this->str_contains($out,'This is routed to by Phad, but contains no items');
    }

    public function testRouted(){
        $out = $this->get('/phad-routed/');

        echo $out;

        $this->str_contains($out,'This is routed to by Phad, but contains no items');
    }

    /**
     * @test that explicitly invoking phad outputs correct data
     */
    public function testExplicit(){
        $out = $this->get('/explicit/');

        echo $out;
        $this->str_contains(
            $out,
            [
                '<li>One</li>',
                '<li>Two</li>',
                '<li>Three</li>',
                '<li>Four</li>',
            ]
        );
    }

    /**
     * @test that the server is running & has basic functionality
     */
    public function testVerify(){
        $out = $this->get('/verify/');

        $this->compare('__Phad__', $out);
    }
}