def test_cc_edge_cases(): cmd = get_calc_cmd() cc = CommandChecker(cmd) with pytest.raises(AttributeError): cc.nonexistentattr with pytest.raises(AttributeError, match='end in integers'): cc.fail_x with pytest.raises(TypeError, match='Container of ints'): cc.run('calc blackjack', exit_code=object()) # disable automatic checking res = cc.run('calc blackjack', input=['20', '20', '1'], exit_code=None) assert res.exit_code == 0 assert res.stderr == 'Bottom card: Retype bottom card: ' # CheckError is also an AssertionError with pytest.raises(AssertionError) as exc_info: cc.run('calc halve nonexistentarg', input='tldr') assert exc_info.value.result.stderr.startswith('error: calc halve: unexpected') with pytest.raises(CheckError): cc.fail('calc halve', input='4') with pytest.raises(CheckError): cc.fail('calc halve', input='4', exit_code=(1, 2))
def test_cc_exc(): cmd = get_calc_cmd() cc_no_reraise = CommandChecker(cmd) res = cc_no_reraise.fail('calc halve', input='4', env={'CALC_TWO': '0'}) assert res.exception assert res.stdout == 'Enter a number: \n' res = cc_no_reraise.fail('calc halve nonexistentarg') assert type(res.exception) is CommandLineError # NB: expect to update these as error messaging improves assert str(res.exception) == "error: calc halve: unexpected positional arguments: ['nonexistentarg']" assert res.stderr.startswith("error: calc halve: unexpected positional arguments: ['nonexistentarg']") assert 'stderr=' in repr(res) with pytest.raises(TypeError): cc_no_reraise.run('calc halve', input=object()) return
def test_err_subcmd_prog_name(): cmd = Command(lambda: print("foo"), "foo") subcmd = Command(lambda: print("bar"), "bar") subcmd.add(Command(lambda: print("baz"), "baz")) cmd.add(subcmd) cc = CommandChecker(cmd) res = cc.fail('fred.py bar ba') assert 'fred.py' in res.stderr assert 'foo' not in res.stderr
def test_cc_getpass(): cmd = get_calc_cmd() cc = CommandChecker(cmd, mix_stderr=True) res = cc.run('calc blackjack', input=['20', '20', '1']) assert res.stdout.endswith('blackjack!\n') # check newline-autoadding behavior when getpass is aborted cc = CommandChecker(cmd) def _raise_eof(*a, **kw): raise EOFError() real_getpass = getpass.getpass try: getpass.getpass = _raise_eof res = cc.fail('calc blackjack') finally: getpass.getpass = real_getpass assert res.stderr.endswith('\n')
def test_cli(tmp_path, _fast_crypto): cmd = cli._get_cmd() cc = CommandChecker(cmd, reraise=True) assert cc.run('pprotect version').stdout.startswith( 'pocket_protector version') tmp_path = str(tmp_path) protected_path = tmp_path + '/protected.yaml' # fail init and ensure that file isn't created cc.fail_1('pprotect init --file %s' % protected_path, input=[KURT_EMAIL, KURT_PHRASE, KURT_PHRASE + 'nope']) assert not os.path.exists(protected_path) # successfully create protected res = cc.run('pprotect init --file %s' % protected_path, input=[KURT_EMAIL, KURT_PHRASE, KURT_PHRASE]) assert res.stdout == 'Adding new key custodian.\nUser email: ' assert res.stderr == 'Passphrase: Retype passphrase: ' # check we can only create it once res = cc.fail_2('pprotect init --file %s' % protected_path, input=[KURT_EMAIL, KURT_PHRASE, KURT_PHRASE]) file_data = ruamel.yaml.YAML().load(open(protected_path).read()) assert list(file_data['key-custodians'])[0] == KURT_EMAIL assert len(file_data['audit-log']) == 2 res = cc.run('pprotect list-audit-log --file %s' % protected_path) audit_lines = res.stdout.splitlines() assert len(audit_lines) == 2 assert 'created' in audit_lines[0] # make a new cc, with env and tmp_path baked in (also tests # protected.yaml in the cur dir being the default file) kurt_env = { 'PPROTECT_USER': KURT_EMAIL, 'PPROTECT_PASSPHRASE': KURT_PHRASE } cc = CommandChecker(cmd, chdir=tmp_path, env=kurt_env, reraise=True) res = cc.run(['pprotect', 'add-domain'], input=[DOMAIN_NAME]) assert 'Adding new domain.' in res.stdout res = cc.run(['pprotect', 'list_domains']) assert res.stdout.splitlines() == [DOMAIN_NAME] cc.run(['pprotect', 'add-secret'], input=[DOMAIN_NAME, SECRET_NAME, 'tmpval']) cc.run(['pprotect', 'update-secret'], input=[DOMAIN_NAME, SECRET_NAME, SECRET_VALUE]) res = cc.run(['pprotect', 'list-domain-secrets', DOMAIN_NAME]) assert res.stdout == SECRET_NAME + '\n' res = cc.run(['pprotect', 'decrypt-domain', DOMAIN_NAME]) res_data = json.loads(res.stdout) assert res_data[SECRET_NAME] == SECRET_VALUE cc.fail(['pprotect', 'decrypt-domain', 'nonexistent-domain']) # already exists cc.fail_1('pprotect add-key-custodian', input=[KURT_EMAIL, '']) cc.run('pprotect add-key-custodian', input=[MH_EMAIL, MH_PHRASE, MH_PHRASE]) cc.run('pprotect add-owner', input=[DOMAIN_NAME, MH_EMAIL]) # missing protected cc.fail_2('pprotect list-all-secrets', chdir=tmp_path + '/..') cc.run('pprotect list-all-secrets') assert SECRET_NAME in res.stdout cc.run(['pprotect', 'rotate_domain_keys'], input=[DOMAIN_NAME]) # test mixed env var and entry res = cc.run(['pprotect', 'decrypt-domain', DOMAIN_NAME], env={ 'PPROTECT_USER': MH_EMAIL, 'PPROTECT_PASSPHRASE': None }, input=[MH_PHRASE]) assert json.loads(res.stdout)[SECRET_NAME] == SECRET_VALUE assert 'Verify passphrase' in res.stderr # test bad creds cc.fail_1(['pprotect', 'decrypt-domain', DOMAIN_NAME], env={ 'PPROTECT_USER': None, 'PPROTECT_PASSPHRASE': 'nope' }, input=[KURT_EMAIL]) res = cc.fail_1( 'pprotect set-key-custodian-passphrase', input=[KURT_EMAIL, KURT_PHRASE, KURT_PHRASE, KURT_PHRASE + 'nope']) assert 'did not match' in res.stderr # correctly reset passphrase new_kurt_phrase = KURT_PHRASE + 'yep' res = cc.run( 'pprotect set-key-custodian-passphrase', input=[KURT_EMAIL, KURT_PHRASE, new_kurt_phrase, new_kurt_phrase]) # try new passphrase with a passphrase file why not ppfile_path = str(tmp_path) + 'tmp_passphrase' with open(ppfile_path, 'wb') as f: f.write(new_kurt_phrase.encode('utf8')) res = cc.run([ 'pprotect', 'decrypt-domain', '--non-interactive', '--passphrase-file', ppfile_path, DOMAIN_NAME ]) res_data = json.loads(res.stdout) assert res_data[SECRET_NAME] == SECRET_VALUE # test mutual exclusivity of check env and interactive cc.fail_2([ 'pprotect', 'decrypt-domain', '--non-interactive', '--ignore-env', DOMAIN_NAME ]) res = cc.fail_1('pprotect decrypt-domain --non-interactive ' + DOMAIN_NAME, env={'PPROTECT_PASSPHRASE': None}) assert 'Warning: Empty passphrase' in res.stderr # print(open(protected_path).read()) # test removals cc.run(['pprotect', 'rm-owner'], input=[DOMAIN_NAME, MH_EMAIL]) cc.run(['pprotect', 'rm-secret'], input=[DOMAIN_NAME, SECRET_NAME]) cc.run(['pprotect', 'rm-domain', '--confirm'], input=[DOMAIN_NAME, 'y'])