Example #1
0
def test_new_session(missing_packages):
    HOST = 'example.com'
    USERNAME = '******'
    PASSWORD = '******'

    # Ensure no dependency on swat required
    with missing_packages('swat'):
        with mock.patch('sasctl.core.Session.get_token'):
            s = Session(HOST, USERNAME, PASSWORD)
        assert USERNAME == s.user
        assert HOST == s.settings['domain']
        assert 'https' == s.settings['protocol']
        assert USERNAME == s.settings['username']
        assert PASSWORD == s.settings['password']

    # Tests don't reset global variables (_session) so explicitly cleanup
    current_session(None)
Example #2
0
def main(args=None):
    """Main entry point when executed as a command line utility."""
    from sasctl import Session, current_session

    # Find all services and associated commands
    services = _find_services()

    parser = _build_parser(services)
    args = parser.parse_args(args)

    if args.verbose is None or args.verbose == 0:
        lvl = logging.WARNING
    elif args.verbose == 1:
        lvl = logging.INFO
    else:
        lvl = logging.DEBUG

    handler = logging.StreamHandler()
    handler.setLevel(lvl)
    logging.getLogger('sasctl.core').addHandler(handler)
    logging.getLogger('sasctl.core').setLevel(lvl)

    warnings.simplefilter('ignore')

    func = services[args.service][args.command]
    kwargs = vars(args).copy()

    # Remove args that shouldn't be passed to the underlying command
    for k in ['command', 'service', 'insecure', 'verbose', 'format']:
        kwargs.pop(k, None)

    username = os.environ.get('SASCTL_USER_NAME')
    password = os.environ.get('SASCTL_PASSWORD')
    server = os.environ.get('SASCTL_SERVER_NAME')

    if server is None:
        parser.error(
            "Hostname must be specified in the 'SASCTL_SERVER_NAME' environment variable."
        )

    verify_ssl = not args.insecure

    try:
        #  current_session() should never be set when executing from the
        #  command line but it allows us to provide a pre-created session
        #  during testing
        with current_session() or Session(
                server, username, password, verify_ssl=verify_ssl):
            result = func(**kwargs)
            if isinstance(result, list):
                pprint([str(x) for x in result])
            elif isinstance(result, dict) and args.format == 'json':
                print(json.dumps(result, indent=2))
            else:
                pprint(result)
    except RuntimeError as e:
        parser.error(e)
Example #3
0
def test_get_model_by_name():
    """If multiple models exist with the same name, a warning should be raised.

    From https://github.com/sassoftware/python-sasctl/issues/92
    """

    MODEL_NAME = 'Test Model'

    # Create a dummy session
    with mock.patch('sasctl.core.requests.Session.request'):
        current_session('example.com', 'user', 'password')

    mock_responses = [
        # First response is for list_items/list_models
        [{
            'id': 12345,
            'name': MODEL_NAME
        }, {
            'id': 67890,
            'name': MODEL_NAME
        }],
        # Second response is mock GET for model details
        {
            'id': 12345,
            'name': MODEL_NAME
        },
    ]

    with mock.patch('sasctl._services.model_repository.ModelRepository.request'
                    ) as request:
        request.side_effect = mock_responses

        with pytest.warns(Warning):
            result = mr.get_model(MODEL_NAME)
    assert result['id'] == 12345
    assert result['name'] == MODEL_NAME
Example #4
0
def session(request, credentials):
    import warnings
    from six.moves import mock
    from betamax.fixtures.pytest import _casette_name
    from sasctl import current_session

    if SKIP_REPLAY:
        yield Session(**credentials)
        current_session(None)
        return

    # Ignore FutureWarnings from betamax to avoid cluttering test results
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        cassette_name = _casette_name(request, parametrized=False)

    # Need to instantiate a Session before starting Betamax recording,
    # but sasctl.Session makes requests (which should be recorded) during
    # __init__().  Mock __init__ to prevent from running and then manually
    # execute requests.Session.__init__() so Betamax can use the session.
    with mock.patch('sasctl.core.Session.__init__', return_value=None):
        recorded_session = Session()
        super(Session, recorded_session).__init__()

    with betamax.Betamax(recorded_session).use_cassette(
            cassette_name, serialize_with='prettyjson') as recorder:
        recorder.start()

        # Manually run the sasctl.Session constructor.  Mock out calls to
        # underlying requests.Session.__init__ to prevent hooks placed by
        # Betamax from being reset.
        with mock.patch('sasctl.core.requests.Session.__init__'):
            recorded_session.__init__(**credentials)
            current_session(recorded_session)
        yield recorded_session
        recorder.stop()
        current_session(None)
Example #5
0
def test_current_session():
    assert current_session() is None

    # Initial session should automatically become the default
    with mock.patch('sasctl.core.Session.get_token'):
        s = Session('example.com', 'user', 'password')
    assert current_session() == s

    # Subsequent sessions should not overwrite the default
    with mock.patch('sasctl.core.Session.get_token'):
        s2 = Session('example.com', 'user2', 'password')
    assert current_session() != s2
    assert current_session() == s

    # Explicitly set new current session
    with mock.patch('sasctl.core.Session.get_token'):
        s3 = current_session('example.com', 'user3', 'password')
    assert current_session() == s3

    # Explicitly change current session
    with mock.patch('sasctl.core.Session.get_token'):
        s4 = Session('example.com', 'user4', 'password')
    current_session(s4)
    assert 'user4' == current_session().user

    with mock.patch('sasctl.core.Session.get_token'):
        with Session('example.com', 'user5', 'password') as s5:
            with Session('example.com', 'user6', 'password') as s6:
                assert current_session() == s6
                assert current_session() != s5
                assert current_session().user == 'user6'
            assert current_session().user == 'user5'
        assert current_session().user == 'user4'
Example #6
0
def test_create_model():

    MODEL_NAME = 'Test Model'
    PROJECT_NAME = 'Test Project'
    PROJECT_ID = '12345'
    USER = '******'

    with mock.patch('sasctl.core.requests.Session.request'):
        current_session('example.com', USER, 'password')

    TARGET = {
        'name':
        MODEL_NAME,
        'projectId':
        PROJECT_ID,
        'modeler':
        USER,
        'description':
        'model description',
        'function':
        'Classification',
        'algorithm':
        'Dummy Algorithm',
        'tool':
        'pytest',
        'champion':
        True,
        'role':
        'champion',
        'immutable':
        True,
        'retrainable':
        True,
        'scoreCodeType':
        None,
        'targetVariable':
        None,
        'trainTable':
        None,
        'classificationEventProbabilityVariableName':
        None,
        'classificationTargetEventValue':
        None,
        'location':
        None,
        'properties': [
            {
                'name': 'custom1',
                'value': 123
            },
            {
                'name': 'custom2',
                'value': 'somevalue'
            },
        ],
        'inputVariables': [],
        'outputVariables': [],
        'version':
        '2',
    }

    # Passed params should be set correctly
    target = copy.deepcopy(TARGET)
    with mock.patch(
            'sasctl._services.model_repository.ModelRepository.get_project'
    ) as get_project:
        with mock.patch('sasctl._services.model_repository.ModelRepository'
                        '.get_model') as get_model:
            with mock.patch(
                    'sasctl._services.model_repository.ModelRepository.post'
            ) as post:
                get_project.return_value = {'id': PROJECT_ID}
                get_model.return_value = None
                _ = mr.create_model(
                    MODEL_NAME,
                    PROJECT_NAME,
                    description=target['description'],
                    function=target['function'],
                    algorithm=target['algorithm'],
                    tool=target['tool'],
                    is_champion=True,
                    is_immutable=True,
                    is_retrainable=True,
                    properties=dict(custom1=123, custom2='somevalue'),
                )
                assert post.call_count == 1
            url, data = post.call_args

            # dict isn't guaranteed to preserve order
            # so k/v pairs of properties=dict() may be
            # returned in a different order
            assert sorted(target['properties'],
                          key=lambda d: d['name']) == sorted(
                              data['json']['properties'],
                              key=lambda d: d['name'])

            target.pop('properties')
            data['json'].pop('properties')
            assert target == data['json']

    # Model dict w/ parameters already specified should be allowed
    # Explicit overrides should be respected.
    target = copy.deepcopy(TARGET)
    with mock.patch(
            'sasctl._services.model_repository.ModelRepository.get_project'
    ) as get_project:
        with mock.patch('sasctl._services.model_repository.ModelRepository'
                        '.get_model') as get_model:
            with mock.patch(
                    'sasctl._services.model_repository.ModelRepository.post'
            ) as post:
                get_project.return_value = {'id': PROJECT_ID}
                get_model.return_value = None
                _ = mr.create_model(copy.deepcopy(target),
                                    PROJECT_NAME,
                                    description='Updated Model')
            target['description'] = 'Updated Model'
            assert post.call_count == 1
            url, data = post.call_args

            # dicts don't preserve order so property order may not match
            assert target['properties'] == data['json']['properties']
            target.pop('properties')
            data['json'].pop('properties')
            assert target == data['json']
def test_create_performance_definition():
    import copy
    from sasctl import current_session

    PROJECT = RestObj({'name': 'Test Project', 'id': '98765'})
    MODEL = RestObj({
        'name': 'Test Model',
        'id': '12345',
        'projectId': PROJECT['id']
    })
    USER = '******'

    with mock.patch('sasctl.core.requests.Session.request'):
        current_session('example.com', USER, 'password')

    with mock.patch('sasctl._services.model_repository.ModelRepository'
                    '.get_model') as get_model:
        with mock.patch('sasctl._services.model_repository.ModelRepository'
                        '.get_project') as get_project:
            with mock.patch('sasctl._services.model_management.ModelManagement'
                            '.post') as post:
                get_model.return_value = MODEL

                with pytest.raises(ValueError):
                    # Project missing all required properties
                    get_project.return_value = copy.deepcopy(PROJECT)
                    _ = mm.create_performance_definition(
                        'model', 'TestLibrary', 'TestData')

                with pytest.raises(ValueError):
                    # Project missing some required properties
                    get_project.return_value = copy.deepcopy(PROJECT)
                    get_project.return_value['targetVariable'] = 'target'
                    _ = mm.create_performance_definition(
                        'model', 'TestLibrary', 'TestData')

                with pytest.raises(ValueError):
                    # Project missing some required properties
                    get_project.return_value = copy.deepcopy(PROJECT)
                    get_project.return_value['targetLevel'] = 'interval'
                    _ = mm.create_performance_definition(
                        'model', 'TestLibrary', 'TestData')

                with pytest.raises(ValueError):
                    # Project missing some required properties
                    get_project.return_value = copy.deepcopy(PROJECT)
                    get_project.return_value[
                        'predictionVariable'] = 'predicted'
                    _ = mm.create_performance_definition(
                        'model', 'TestLibrary', 'TestData')

                get_project.return_value = copy.deepcopy(PROJECT)
                get_project.return_value['targetVariable'] = 'target'
                get_project.return_value['targetLevel'] = 'interval'
                get_project.return_value['predictionVariable'] = 'predicted'
                _ = mm.create_performance_definition('model',
                                                     'TestLibrary',
                                                     'TestData',
                                                     max_bins=3,
                                                     monitor_challenger=True,
                                                     monitor_champion=True)

            assert post.call_count == 1
            url, data = post.call_args

            assert PROJECT['id'] == data['json']['projectId']
            assert MODEL['id'] in data['json']['modelIds']
            assert 'TestLibrary' == data['json']['dataLibrary']
            assert 'TestData' == data['json']['dataPrefix']
            assert 'cas-shared-default' == data['json']['casServerId']
            assert data['json']['name'] is not None
            assert data['json']['description'] is not None
            assert data['json']['maxBins'] == 3
            assert data['json']['championMonitored'] == True
            assert data['json']['challengerMonitored'] == True

    def test_table_prefix_format():
        with pytest.raises(ValueError):
            # Underscores should not be allowed
            _ = mm.create_performance_definition('model', 'TestLibrary',
                                                 'invalid_name')
Example #8
0
#!/usr/bin/env python
# encoding: utf-8
#
# Copyright © 2019, SAS Institute Inc., Cary, NC, USA.  All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

import pytest
from six.moves import mock

from sasctl.services import microanalytic_score as mas

from sasctl import current_session
from sasctl.core import RestObj

with mock.patch('sasctl.core.requests.Session.request'):
    current_session('example.com', 'username', 'password')


def test_create_python_module():
    with mock.patch('sasctl.services.microanalytic_score.post') as post:
        with pytest.raises(ValueError):
            mas.create_module()  # Source code is required

    with mock.patch('sasctl.services.microanalytic_score.post') as post:
        source = '\n'.join(
            ("def testMethod(var1, var2):", "    'Output: out1, out2'",
             "    out1 = var1 + 5", "    out2 = var2.upper()",
             "    return out1, out2"))
        mas.create_module(source=source)

        assert post.call_count == 1