InterfaceTest.php #1

  • //
  • guest/
  • thomas_gray/
  • jambox/
  • main/
  • swarm/
  • tests/
  • phpunit/
  • P4Test/
  • Connection/
  • InterfaceTest.php
  • View
  • Commits
  • Open Download .zip Download (21 KB)
<?php
/**
 * Test methods for the P4 api interface.
 *
 * @copyright   2012 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */

namespace P4Test\Connection;

use P4Test\TestCase;
use P4\Connection\Connection;
use P4\Connection\Exception\LoginException;
use P4\Spec\User;

abstract class InterfaceTest extends TestCase
{
    /**
     * Test that connecting / disconnecting works.
     */
    public function testConnectAndDisconnect()
    {
        $this->p4->connect();
        $this->assertTrue($this->p4->isConnected());
        $this->p4->disconnect();
        $this->assertFalse($this->p4->isConnected());

        // try connecting to a bogus server.
        // should throw an exception.
        $this->p4->setPort('laskdjfskdlafj:4231');
        try {
            $this->p4->connect();
            $this->fail('Expect failure with a bogus server.');
        } catch (\P4\Connection\Exception\ConnectException $e) {
            $this->assertTrue(true);
        } catch (\Exception $e) {
            $this->fail('Unexpected exception: '. $e->getMessage());
        }
    }

    /**
     * Test setting/getting port.
     */
    public function testPort()
    {
        $this->p4->setPort('hostname.something.com:5555');
        $this->assertSame('hostname.something.com:5555', $this->p4->getPort(), 'Expect just-set port');

        // test port via constructor.
        $type = get_class($this->p4);
        $p4   = Connection::factory('1234', null, null, null, null, $type);
        $this->assertSame($p4->getPort(), '1234', 'Expect factory-set port.');

        // getPort should always return a string
        $this->p4->setPort(3333);
        $this->assertTrue(is_string($this->p4->getPort()));
        $this->assertSame($this->p4->getPort(), '3333', 'Expect correct string.');
        $this->assertEquals($this->p4->getPort(), 3333, 'Expect correct numeric value.');
        $this->assertNotSame($this->p4->getPort(), 3333, 'Expect port to be non-numeric.');
    }

    /**
     * Test setting/getting user.
     */
    public function testUser()
    {
        $this->p4->setUser('john_doe');
        $this->assertSame($this->p4->getUser(), 'john_doe', 'Expected user.');

        // test setting user via constructor.
        $type = get_class($this->p4);
        $p4   = Connection::factory(null, 'jdoe', null, null, null, $type);
        $this->assertSame($p4->getUser(), 'jdoe', 'Expected factory user.');

        // test user name acceptance
        $tests = array(
            // valid cases
            array('name' => 'jdoe',   'valid' => true),
            array('name' => 'jdoe.',  'valid' => true),
            array('name' => 'jdoe..', 'valid' => true),
            array('name' => 'j_doe',  'valid' => true),
            array('name' => 'jdoe"',  'valid' => true),
            array('name' => "jdoe'",  'valid' => true),
            array('name' => 'jdoe&',  'valid' => true),
            array('name' => 'jdoe1',  'valid' => true),
            array('name' => 'jdoe%%', 'valid' => true),
            array('name' => 'j/doe',  'valid' => true),

            // invalid cases
            array(
                'name'  => '1234',
                'valid' => false,
                'error' => 'Username: Purely numeric values are not allowed.'
            ),
            array(
                'name'  => 'john doe',
                'valid' => false,
                'error' => 'Username: Whitespace is not permitted.'
            ),
            array(
                'name'  => 'jdoe*',
                'valid' => false,
                'error' => "Username: Wildcards ('*', '...') are not permitted."
            ),
            array(
                'name'  => 'jdoe...',
                'valid' => false,
                'error' => "Username: Wildcards ('*', '...') are not permitted."
            ),
            array(
                'name'  => 'jdoe#',
                'valid' => false,
                'error' => "Username: Revision characters ('#', '@') are not permitted."
            ),
            array(
                'name'  => 'jdoe@',
                'valid' => false,
                'error' => "Username: Revision characters ('#', '@') are not permitted."
            ),
        );
        foreach ($tests as $test) {
            try {
                $this->p4->setUser($test['name']);
                if (!$test['valid']) {
                    $this->fail("Expected '". $test['name'] ."' to fail.");
                }
            } catch (\P4\Exception $e) {
                if ($test['valid']) {
                    $this->fail("Expected '". $test['name'] ."' to succeed: ". $e->getMessage());
                } else {
                    $this->assertEquals(
                        $test['error'],
                        $e->getMessage(),
                        'Expected error message'
                    );
                }
            } catch (\Exception $e) {
                $this->fail('Unexpected exception: '. $e->getMessage());
            }
        }
    }

    /**
     * Test setting/getting client.
     */
    public function testClient()
    {
        $this->p4->setClient('test-client');
        $this->assertSame('test-client', $this->p4->getClient(), 'Expected client.');
        $this->assertNotSame('blah', $this->p4->getClient(), 'Unexpected client.');

        // test setting client via constructor.
        $type = get_class($this->p4);
        $p4   = Connection::factory(null, null, 'jdoes_client', null, null, $type);
        $this->assertSame($p4->getClient(), 'jdoes_client', 'Expected factory client.');

        // test user name acceptance
        $tests = array(
            // valid cases
            array('client' => 'johnsclient',    'valid' => true),
            array('client' => 'johnsClient',    'valid' => true),
            array('client' => 'johns_client',   'valid' => true),
            array('client' => 'johns_client.',  'valid' => true),
            array('client' => 'johns_client..', 'valid' => true),
            array('client' => 'johns_client"',  'valid' => true),
            array('client' => "johns_client'",  'valid' => true),
            array('client' => 'johns_client&',  'valid' => true),
            array('client' => 'johns_client1',  'valid' => true),

            // invalid cases
            array(
                'client' => '1234',
                'valid'  => false,
                'error'  => 'Client name: Purely numeric values are not allowed.'
            ),
            array(
                'client' => 'johns client',
                'valid'  => false,
                'error'  => 'Client name: Whitespace is not permitted.'
            ),
            array(
                'client' => 'johns_client*',
                'valid'  => false,
                'error' => "Client name: Wildcards ('*', '...') are not permitted."
            ),
            array(
                'client' => 'johns_client...',
                'valid'  => false,
                'error' => "Client name: Wildcards ('*', '...') are not permitted."
            ),
            array(
                'client' => 'johns_client%%',
                'valid'  => false,
                'error' => "Client name: Positional specifiers ('%%x') are not permitted."
            ),
            array(
                'client' => 'johns_client#',
                'valid'  => false,
                'error'  => "Client name: Revision characters ('#', '@') are not permitted."
            ),
            array(
                'client' => 'johns_client@',
                'valid'  => false,
                'error'  => "Client name: Revision characters ('#', '@') are not permitted."
            ),
        );
        foreach ($tests as $test) {
            try {
                $this->p4->setClient($test['client']);
                if (!$test['valid']) {
                    $this->fail("Expected '". $test['client'] ."' to fail.");
                }
            } catch (\P4\Exception $e) {
                if ($test['valid']) {
                    $this->fail("Expected '". $test['client'] ."' to succeed: ". $e->getMessage());
                } else {
                    $this->assertEquals(
                        $test['error'],
                        $e->getMessage(),
                        'Expected error message'
                    );
                }
            } catch (\Exception $e) {
                $this->fail('Unexpected exception: '. $e->getMessage());
            }
        }
    }

    /**
     * Test setting/getting password.
     */
    public function testPassword()
    {
        $this->p4->setPassword('test-password');
        $this->assertSame('test-password', $this->p4->getPassword());
        $this->assertNotSame('blah', $this->p4->getPassword());

        // test setting password via constructor.
        $type = get_class($this->p4);
        $p4   = Connection::factory(null, null, null, 'secret key', null, $type);
        $this->assertTrue($p4->getPassword() == 'secret key');
    }


    /**
     * Test setting/getting ticket.
     */
    public function testTicket()
    {
        $this->p4->setTicket('ALKSJROIEL2134235');
        $this->assertSame('ALKSJROIEL2134235', $this->p4->getTicket(), 'Expected ticket.');
        $this->assertNotSame('blah', $this->p4->getTicket(), 'Unexpected ticket.');

        // test setting ticket via constructor.
        $type = get_class($this->p4);
        $p4   = Connection::factory(null, null, null, null, 'ALKSJROIEL2134235', $type);
        $this->assertSame($p4->getTicket(), 'ALKSJROIEL2134235', 'Expected factory ticket.');
    }

    /**
     * Test Connection identity.
     */
    public function testConnectionIdentity()
    {
        $identity = $this->p4->getConnectionIdentity();
        $this->assertTrue(isset($identity['name']), 'Expect name is set.');
        $this->assertTrue(isset($identity['platform']), 'Expect platform is set.');
        $this->assertTrue(isset($identity['version']), 'Expect version is set.');
        $this->assertTrue(isset($identity['build']), 'Expect build is set.');
        $this->assertTrue(isset($identity['apiversion']), 'Expect apiversion is set.');
        $this->assertTrue(isset($identity['apibuild']), 'Expect apibuild is set.');
        $this->assertTrue(isset($identity['date']), 'Expect date is set.');
        $this->assertTrue(isset($identity['original']), 'Expect original is set.');
    }

    /**
     * Test get info.
     */
    public function testGetInfo()
    {
        $info = $this->p4->getInfo();
        $this->assertTrue(is_array($info), 'Expect info to be an array.');
        $this->assertTrue(isset($info['userName']), 'Expect userName is set.');
        $this->assertTrue(isset($info['clientName']), 'Expect clientName is set.');
        $this->assertTrue(isset($info['clientCwd']), 'Expect clientCwd is set.');
        $this->assertTrue(isset($info['clientHost']), 'Expect clientHost is set.');
        $this->assertTrue(isset($info['clientAddress']), 'Expect clientAddress is set.');
        $this->assertTrue(isset($info['serverAddress']), 'Expect serverAddress is set.');
        $this->assertTrue(isset($info['serverRoot']), 'Expect serverRoot is set.');
        $this->assertTrue(isset($info['serverDate']), 'Expect serverDate is set.');
        $this->assertTrue(isset($info['serverUptime']), 'Expect serverUptime is set.');
        $this->assertTrue(isset($info['serverVersion']), 'Expect serverVersion is set.');
        $this->assertTrue(isset($info['serverLicense']), 'Expect serverLicense is set.');

        // test that cache is cleared when connection params change.
        $this->p4->setClient('a-client');
        $info2 = $this->p4->getInfo();
        $this->assertNotEquals(serialize($info), serialize($info2), 'Unexpected client match.');
    }

    /**
     * Test client root accessor.
     */
    public function testGetClientRoot()
    {
        $root = $this->p4->getClientRoot();
        $info = $this->p4->getInfo();
        if ($root) {
            $this->assertSame($root, $info['clientRoot'], 'Expect root to match info.');
        } else {
            $this->assertFalse(isset($info['clientRoot']), 'Unexpected clientRoot with no root.');
        }
    }

    /**
     * Test login/authentication.
     */
    public function testLogin()
    {
        $this->assertTrue(
            $this->p4->isAuthenticated(),
            'Expected user to be authenticated'
        );

        try {
            $ticket = $this->p4->login();
            $this->assertTrue(strlen($ticket) > 0, "Expected login ticket");
        } catch (LoginException $e) {
            $this->fail("Expected login to succeed");
        }

        try {
            $this->p4->setPassword('alskdfj23523');
            $ticket = $this->p4->login();
            $this->fail("Expected login failure");
        } catch (LoginException $e) {
            $this->assertSame(
                LoginException::CREDENTIAL_INVALID,
                $e->getCode(),
                "Expected credential invalid login exception"
            );
        }

        // erase the successful login ticket so that the password is re-evaluated
        $this->p4->setTicket(null);
        $this->assertFalse(
            $this->p4->isAuthenticated(),
            'Expected user not to be authenticated'
        );

        try {
            $this->p4->setUser('laksdjflkasdfj');
            $ticket = $this->p4->login();
            $this->fail("Expected login failure");
        } catch (LoginException $e) {
            $this->assertSame(
                LoginException::IDENTITY_NOT_FOUND,
                $e->getCode(),
                "Expected identity not found login exception"
            );
        }
    }

    /**
     * Test running a command.
     */
    public function testRun()
    {
        $result = $this->p4->run('users', null, null, false);
        $this->assertFalse($result->isTagged(), 'Expect untagged result.');

        $result = $this->p4->run('users');
        $this->assertTrue($result->isTagged(), 'Expect tagger result.');

        $data = $result->getData();
        $this->assertSame(
            serialize($result->getData(0)),
            serialize($data[0]),
            'Expect getData match.'
        );
        $this->assertSame(
            $result->getData(0, 'User'),
            $data[0]['User'],
            'Expect getData User match.'
        );
        $this->assertSame($result->getCommand(), 'users', 'Expect command match.');
    }

    /**
     * Test super user detection.
     */
    public function testSuperUser()
    {
        // instance _p4 object runs as super.
        $this->assertTrue($this->p4->isSuperUser(), 'Connection should have super user privs.');

        // create un-privileged user.
        $user = $this->p4->run('user', array('-o', 'jdoe'));
        $this->p4->run('user', array('-i', '-f'), $user->getData(-1));

        // connect as un-privileged user.
        $class = get_class($this->p4);
        $p4 = new $class;
        $p4->setUser('jdoe');
        $p4->setPort($this->p4->getPort());
        $p4->connect();
        $this->assertFalse($p4->isSuperUser(), 'Connection should not have super user privs.');
    }

    /**
     * Test the security level.
     */
    public function testSecurityLevel()
    {
        // should be zero to start.
        $this->assertTrue($this->p4->getSecurityLevel() == 0, "Expected security level zero");

        $counter = new \P4\Counter\Counter;
        $counter->setId('security');

        // 1
        $counter->set(1, true);
        $this->assertTrue($this->p4->getSecurityLevel() == 1, "Expected security level one");

        // 2
        $counter->set(2, true);

        // once the security counter is increased to 2, the current user's password must be reset.
        // see the Perforce System Administrator's Guide, Chapter 3, Server Security Levels
        // http://www.perforce.com/perforce/doc.current/manuals/p4sag/03_superuser.html#1081537
        //
        // Unfortunately, this doesn't work the way you'd hope. The desire would be to write
        // something similar to:
        //
        //     $user = \P4\Spec\User::fetch($this->p4->getUser())
        //                    ->setPassword('testing321')
        //                    ->save();
        //
        // Calling setPassword() invokes other Perforce commands prior to the password command
        // (for lazy loading, verifying protections, etc.), and these commands will fail due
        // to the password reset requirement.
        //
        // So, we need to directly execute the password command.
        $newPassword = 'newPassword123';
        $this->p4->run(
            'password',
            null,
            array(
                $this->p4->getPassword(),
                $newPassword,
                $newPassword
            )
        );
        $this->p4->setPassword($newPassword);

        $this->assertTrue($this->p4->getSecurityLevel() == 2, "Expected security level two");

        // 3
        $counter->set(3, true);

        // must login.
        $this->p4->login();

        $this->assertTrue($this->p4->getSecurityLevel() == 3, "Expected security level three");
    }

    /**
     * Test app name
     */
    public function testAppName()
    {
        $root = $this->getP4Params('serverRoot');
        $port = $this->p4->getPort() . ' -vrpc=3 -L ' . $root . '/test-log';
        $p4   = Connection::factory(
            $port,
            $this->getP4Params('user'),
            $this->getP4Params('client'),
            $this->getP4Params('password')
        );

        // verify can set/get app name
        $p4->setAppName('some-name');
        $this->assertSame('some-name', $p4->getAppName());

        // verify can set/get program name
        $p4->setProgName('my-prog');
        $this->assertSame('my-prog', $p4->getProgName());

        // verify can set/get program version
        $p4->setProgVersion('2013.1.WHATEVER/123456');
        $this->assertSame('2013.1.WHATEVER/123456', $p4->getProgVersion());

        // verify server sees app name
        $p4->getInfo();
        $log = file_get_contents($root . '/test-log');
        $this->assertTrue(strpos($log, 'app = some-name') !== false, "Looking for app name in log.");
    }

    /**
     * Test the service locator facilities of the connection
     */
    public function testServiceLocator()
    {
        $p4 = $this->p4;

        // should throw for non-existent services
        try {
            $p4->getService('foo');
            $this->fail();
        } catch (\P4\Connection\Exception\ServiceNotFoundException $e) {
            $this->assertTrue(true);
        }

        // should require a object or callable
        try {
            $p4->setService('foo', 'bar');
            $this->fail();
        } catch (\InvalidArgumentException $e) {
            $this->assertTrue(true);
        }

        // configure a basic service
        $service = new \stdClass;
        $service->value = true;
        $p4->setService('foo', $service);
        $this->assertSame($service, $p4->getService('foo'));

        // configure a service factory
        $factory = function ($p4, $name) {
            $service = new \stdClass;
            $service->p4    = $p4;
            $service->name  = $name;
            return $service;
        };
        $p4->setService('baz', $factory);
        $service = $p4->getService('baz');
        $this->assertTrue($service instanceof \stdClass);
        $this->assertSame($p4,   $service->p4);
        $this->assertSame('baz', $service->name);

        // once created, service should be reused
        $this->assertTrue($service === $p4->getService('baz'));
    }

    public function testIsAuthenticated()
    {
        $user = new User($this->p4);
        $user->setId('jdoe');
        $user->setPassword('abc123');
        $user->setEmail('[email protected]');
        $user->setFullName('Jonathan H. Doe');
        $user->save();

        $user2 = new User($this->p4);
        $user2->setId('jdoe2');
        $user2->setEmail('[email protected]');
        $user2->setFullName('Jonathan H. DEUX');
        $user2->save();

        // test valid password
        $p4 = Connection::factory($this->p4->getPort(), 'jdoe', null, 'abc123');
        $this->assertTrue($p4->isAuthenticated());

        // test invalid password
        $p4 = Connection::factory($this->p4->getPort(), 'jdoe', null, 'xyz789');
        $this->assertFalse($p4->isAuthenticated());

        // test valid ticket
        $p4 = Connection::factory($this->p4->getPort(), 'jdoe', null, 'abc123');
        $p4->login();
        $p4->setPassword(null);
        $this->assertNotNull($p4->getTicket());
        $p4->setTicket($p4->getTicket());
        $this->assertTrue($p4->isAuthenticated());

        // test invalid ticket
        $ticket = $p4->getTicket();
        $p4->run('logout');
        $p4->disconnect();
        $this->assertFalse($p4->isAuthenticated());

        // test edge case of valid blank password
        $p4 = Connection::factory($this->p4->getPort(), 'jdoe2', null, null);
        $this->assertTrue($p4->isAuthenticated());

        // test edge case of invalid password (when login not required)
        $p4 = Connection::factory($this->p4->getPort(), 'jdoe2', null, 'abc123');
        $this->assertFalse($p4->isAuthenticated());

        // test invalid ticket (recycling invalid ticket from above)
        $p4 = Connection::factory($this->p4->getPort(), 'jdoe2', null, null, $ticket);
        $this->assertFalse($p4->isAuthenticated());
    }
}
# Change User Description Committed
#1 18730 Liz Lam clean up code and move things around