Skip to content

Vetiver tests #331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,29 @@ jobs:
repo: 'rsconnect-jupyter',
event_type: 'rsconnect_python_latest'
})
test-rsconnect:
name: "Test vetiver"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m pip install -r vetiver-testing/vetiver-requirements.txt
- name: run RStudio Connect
run: |
docker-compose up --build -d
pip freeze > requirements.txt
make dev
env:
RSC_LICENSE: ${{ secrets.RSC_LICENSE }}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

# NOTE: edited to run checks for python package
- name: Run tests
run: |
pytest -m 'vetiver'
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ endif

TEST_ENV =

RSC_API_KEYS=vetiver-testing/rsconnect_api_keys.json

ifneq ($(CONNECT_SERVER),)
TEST_ENV += CONNECT_SERVER=$(CONNECT_SERVER)
endif
Expand Down Expand Up @@ -165,3 +167,21 @@ promote-docs-in-s3:
--cache-control max-age=300 \
docs/site/ \
s3://docs.rstudio.com/rsconnect-python/


dev: vetiver-testing/rsconnect_api_keys.json

dev-start:
docker-compose up -d
docker-compose exec -T rsconnect bash < vetiver-testing/setup-rsconnect/add-users.sh
# curl fails with error 52 without a short sleep....
sleep 5
curl -s --retry 10 --retry-connrefused http://localhost:3939

dev-stop:
docker-compose down
rm -f $(RSC_API_KEYS)

$(RSC_API_KEYS): dev-start
python vetiver-testing/setup-rsconnect/dump_api_keys.py $@

15 changes: 15 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import sys
import pytest

from os.path import abspath, dirname


HERE = dirname(abspath(__file__))
sys.path.insert(0, HERE)


def pytest_addoption(parser):
parser.addoption(
"--vetiver", action="store_true", default=False, help="run vetiver tests"
)

def pytest_configure(config):
config.addinivalue_line("markers", "vetiver: test for vetiver interaction")

def pytest_collection_modifyitems(config, items):
if config.getoption("--vetiver"):
return
skip_vetiver = pytest.mark.skip(reason="need --vetiver option to run")
17 changes: 17 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: '3.2'

services:

rsconnect:
image: rstudio/rstudio-connect:latest
restart: always
ports:
- 3939:3939
volumes:
- $PWD/vetiver-testing/setup-rsconnect/users.txt:/etc/users.txt
- $PWD/vetiver-testing/setup-rsconnect/rstudio-connect.gcfg:/etc/rstudio-connect/rstudio-connect.gcfg
# by default, mysql rounds to 4 decimals, but tests require more precision
privileged: true
environment:
RSTUDIO_CONNECT_HASTE: "enabled"
RSC_LICENSE: ${RSC_LICENSE}
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ omit = ["tests/*"]

[tool.setuptools_scm]
write_to = "rsconnect/version.py"

[tool.pytest.ini_options]
markers = [
"vetiver: tests for vetiver",
]
95 changes: 95 additions & 0 deletions tests/test_vetiver_pins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import pytest

vetiver = pytest.importorskip("vetiver", reason="vetiver library not installed")

import json # noqa
import pins # noqa
import pandas as pd # noqa
import numpy as np # noqa

from pins.boards import BoardRsConnect # noqa
from pins.rsconnect.api import RsConnectApi # noqa
from pins.rsconnect.fs import RsConnectFs # noqa
from rsconnect.api import RSConnectServer, RSConnectClient # noqa

RSC_SERVER_URL = "http://localhost:3939"
RSC_KEYS_FNAME = "vetiver-testing/rsconnect_api_keys.json"

pytestmark = pytest.mark.vetiver # noqa


def get_key(name):
with open(RSC_KEYS_FNAME) as f:
api_key = json.load(f)[name]
return api_key


def rsc_from_key(name):
with open(RSC_KEYS_FNAME) as f:
api_key = json.load(f)[name]
return RsConnectApi(RSC_SERVER_URL, api_key)


def rsc_fs_from_key(name):

rsc = rsc_from_key(name)

return RsConnectFs(rsc)


def rsc_delete_user_content(rsc):
guid = rsc.get_user()["guid"]
content = rsc.get_content(owner_guid=guid)
for entry in content:
rsc.delete_content_item(entry["guid"])


@pytest.fixture(scope="function")
def rsc_short():
# tears down content after each test
fs_susan = rsc_fs_from_key("susan")

# delete any content that might already exist
rsc_delete_user_content(fs_susan.api)

yield BoardRsConnect("", fs_susan, allow_pickle_read=True) # fs_susan.ls to list content

rsc_delete_user_content(fs_susan.api)


def test_deploy(rsc_short):
np.random.seed(500)

# Load data, model
X_df, y = vetiver.mock.get_mock_data()
model = vetiver.mock.get_mock_model().fit(X_df, y)

v = vetiver.VetiverModel(model=model, ptype_data=X_df, model_name="susan/model")

board = pins.board_rsconnect(server_url=RSC_SERVER_URL, api_key=get_key("susan"), allow_pickle_read=True)

vetiver.vetiver_pin_write(board=board, model=v)
connect_server = RSConnectServer(url=RSC_SERVER_URL, api_key=get_key("susan"))

vetiver.deploy_rsconnect(
connect_server=connect_server,
board=board,
pin_name="susan/model",
title="testapivetiver",
extra_files=["requirements.txt"],
)

# get url of where content lives
client = RSConnectClient(connect_server)
dicts = client.content_search()
rsc_api = list(filter(lambda x: x["title"] == "testapivetiver", dicts))
content_url = rsc_api[0].get("content_url")

h = {"Authorization": 'Key {}'.format(get_key("susan"))}

endpoint = vetiver.vetiver_endpoint(content_url + "/predict")
response = vetiver.predict(endpoint, X_df, headers=h)

assert isinstance(response, pd.DataFrame), response
assert response.iloc[0, 0] == 44.47
assert len(response) == 100
1 change: 1 addition & 0 deletions vetiver-testing/setup-rsconnect/add-users.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
awk ' { system("useradd -m -s /bin/bash "$1); system("echo \""$1":"$2"\" | chpasswd"); system("id "$1) } ' /etc/users.txt
21 changes: 21 additions & 0 deletions vetiver-testing/setup-rsconnect/dump_api_keys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import json
import sys

from pins.rsconnect.api import _HackyConnect

OUT_FILE = sys.argv[1]


def get_api_key(user, password, email):
rsc = _HackyConnect("http://localhost:3939")

return rsc.create_first_admin(user, password, email).api_key


api_keys = {
"admin": get_api_key("admin", "admin0", "admin@example.com"),
"susan": get_api_key("susan", "susan", "susan@example.com"),
"derek": get_api_key("derek", "derek", "derek@example.com"),
}

json.dump(api_keys, open(OUT_FILE, "w"))
23 changes: 23 additions & 0 deletions vetiver-testing/setup-rsconnect/rstudio-connect.gcfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[Server]
DataDir = /data
Address = http://localhost:3939

[HTTP]
Listen = :3939

[Authentication]
Provider = pam

[Authorization]
DefaultUserRole = publisher

[Python]
Enabled = true
Executable = /opt/python/3.8.10/bin/python
Executable = /opt/python/3.9.5/bin/python

[RPackageRepository "CRAN"]
URL = https://packagemanager.rstudio.com/cran/__linux__/bionic/latest

[RPackageRepository "RSPM"]
URL = https://packagemanager.rstudio.com/cran/__linux__/bionic/latest
4 changes: 4 additions & 0 deletions vetiver-testing/setup-rsconnect/users.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
admin admin0
test test
susan susan
derek derek
5 changes: 5 additions & 0 deletions vetiver-testing/vetiver-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pins
pandas
numpy
vetiver
pytest