Skip to content
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

GitHub Actions #24

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
44 changes: 44 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Run tests

on:
push:
branches: [master, develop]
pull_request:
branches: [master, develop]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10']

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r test_requirements.txt
- name: Create settings, whitelist, and log files
run: |
cp settings.py.template settings.py
cp whitelist.json.template whitelist.json
mkdir logs; touch logs/faculty-tools.log
- name: Lint with flake8
run: flake8
- name: Check formatting
uses: psf/black@stable
- name: Check import sorting
uses: jamescurtin/isort-action@master
- name: Lint markdown files
uses: bewuethr/mdl-action@v1
- name: Run tests
run: coverage run -m unittest discover
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
23 changes: 0 additions & 23 deletions .travis.yml

This file was deleted.

18 changes: 9 additions & 9 deletions lti.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
from logging import Formatter, INFO
from logging.handlers import RotatingFileHandler
import json
import os
import time
from logging import INFO, Formatter
from logging.handlers import RotatingFileHandler

import jinja2
import requests
from canvasapi.exceptions import CanvasException
from flask import (
Flask,
Response,
redirect,
render_template,
session,
request,
redirect,
url_for,
Response,
send_from_directory,
session,
url_for,
)
from flask_sqlalchemy import SQLAlchemy
import jinja2
from pylti.flask import lti
import requests
from requests.exceptions import HTTPError

from utils import filter_tool_list, slugify
import settings
from utils import filter_tool_list, slugify

app = Flask(__name__)
app.config.from_object(settings.configClass)
Expand Down
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[tool.black]
line-length = 88
target_version = ['py37', 'py38', 'py39']
exclude = "src"

[tool.isort]
profile = "black"
skip = "src"
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
canvasapi==0.15.0
Flask==1.1.1
Flask-SQLAlchemy==2.4.1
canvasapi==3.0.0
Flask==2.2.2
Flask-SQLAlchemy==3.0.2
mysqlclient
-e git+https://github.com/ucfcdl/pylti.git@roles#egg=PyLTI
git+https://github.com/ucfcdl/pylti.git@roles#egg=PyLTI
requests==2.22.0
Werkzeug>=1.0.1 # Chrome 80 SameSite fix
6 changes: 1 addition & 5 deletions settings.py.template
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ SHARED_SECRET = "secret"

# Configuration for pylti library. Uses the above key and secret
PYLTI_CONFIG = {
"consumers": {
CONSUMER_KEY: {
"secret": SHARED_SECRET
}
},
"consumers": {CONSUMER_KEY: {"secret": SHARED_SECRET}},
# Custom configurable roles
"roles": {
"staff": [
Expand Down
3 changes: 1 addition & 2 deletions setup.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
source env/bin/activate
export FLASK_APP=lti.py
export FLASK_ENV=development
export FLASK_DEBUG=1
1 change: 1 addition & 0 deletions test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ blinker
coverage
flake8
Flask-Testing>=0.8.0
isort
mock
oauthlib
requests-mock
42 changes: 27 additions & 15 deletions tests.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from json.decoder import JSONDecodeError
import logging
import time
import unittest
from json.decoder import JSONDecodeError
from urllib.parse import urlencode

import canvasapi
import oauthlib.oauth1
import flask
from flask import url_for
import flask_testing
import oauthlib.oauth1
import requests_mock
from flask import url_for
from mock import mock_open, patch
from pylti.common import LTI_SESSION_KEY
import time

from mock import patch, mock_open
import lti
import settings
import utils
Expand Down Expand Up @@ -86,6 +86,18 @@ def generate_launch_request(
new_url = signed_url[len(base_url) :]
return new_url

def assert_redirects_new(self, response, location, message=None):
valid_status_codes = (301, 302, 303, 305, 307)
valid_status_code_str = ", ".join(str(code) for code in valid_status_codes)
not_redirect = "HTTP Status %s expected but got %d" % (
valid_status_code_str,
response.status_code,
)
self.assertTrue(
response.status_code in valid_status_codes, message or not_redirect
)
self.assertEqual(response.location, location, message)

def test_select_theme_dirs(self, m):
theme_dirs = lti.select_theme_dirs()

Expand Down Expand Up @@ -257,7 +269,7 @@ def test_index_api_key_expired(self, m):
redirect_url = (
"{}login/oauth2/auth?client_id={}&response_type=code&redirect_uri={}"
)
self.assert_redirects(
self.assert_redirects_new(
response,
redirect_url.format(
settings.BASE_URL, settings.oauth2_id, settings.oauth2_uri
Expand Down Expand Up @@ -297,7 +309,7 @@ def test_index_api_key_404(self, m):
redirect_url = (
"{}login/oauth2/auth?client_id={}&response_type=code&redirect_uri={}"
)
self.assert_redirects(
self.assert_redirects_new(
response,
redirect_url.format(
settings.BASE_URL, settings.oauth2_id, settings.oauth2_uri
Expand Down Expand Up @@ -592,7 +604,7 @@ def test_oauth_login_new_user(self, m):
)
)

self.assert_redirects(response, url_for("index"))
self.assert_redirects_new(response, url_for("index"))

# Check that user is created
user = lti.Users.query.filter_by(
Expand Down Expand Up @@ -701,7 +713,7 @@ def test_oauth_login_existing_user(self, m):
)
)

self.assert_redirects(response, url_for("index"))
self.assert_redirects_new(response, url_for("index"))
self.assertGreater(user.expires_in, old_expire)

def test_oauth_login_existing_user_db_error(self, m):
Expand Down Expand Up @@ -905,7 +917,7 @@ def test_auth_no_user(self, m):
redirect_url = (
"{}login/oauth2/auth?client_id={}&response_type=code&redirect_uri={}"
)
self.assert_redirects(
self.assert_redirects_new(
response,
redirect_url.format(
settings.BASE_URL, settings.oauth2_id, settings.oauth2_uri
Expand Down Expand Up @@ -950,7 +962,7 @@ def test_auth_no_api_key_refresh_success(self, m, mock_refresh_access_token):
self.assertEqual(flask.session["api_key"], new_access_token)
self.assertEqual(flask.session["expires_in"], new_expiry_date)

self.assert_redirects(response, url_for("index"))
self.assert_redirects_new(response, url_for("index"))

@patch("lti.refresh_access_token")
def test_auth_no_api_key_refresh_fail(self, m, mock_refresh_access_token):
Expand Down Expand Up @@ -987,7 +999,7 @@ def test_auth_no_api_key_refresh_fail(self, m, mock_refresh_access_token):
redirect_url = (
"{}login/oauth2/auth?client_id={}&response_type=code&redirect_uri={}"
)
self.assert_redirects(
self.assert_redirects_new(
response,
redirect_url.format(
settings.BASE_URL, settings.oauth2_id, settings.oauth2_uri
Expand Down Expand Up @@ -1038,7 +1050,7 @@ def test_auth_invalid_api_key_refresh_success(self, m, mock_refresh_access_token
data=payload,
)

self.assertRedirects(response, url_for("index"))
self.assert_redirects_new(response, url_for("index"))

@patch("lti.refresh_access_token")
def test_auth_invalid_api_key_refresh_fail(self, m, mock_refresh_access_token):
Expand Down Expand Up @@ -1085,7 +1097,7 @@ def test_auth_invalid_api_key_refresh_fail(self, m, mock_refresh_access_token):
redirect_url = (
"{}login/oauth2/auth?client_id={}&response_type=code&redirect_uri={}"
)
self.assert_redirects(
self.assert_redirects_new(
response,
redirect_url.format(
settings.BASE_URL, settings.oauth2_id, settings.oauth2_uri
Expand Down Expand Up @@ -1123,7 +1135,7 @@ def test_auth(self, m):
data=payload,
)

self.assert_redirects(response, url_for("index"))
self.assert_redirects_new(response, url_for("index"))

# get_sessionless_url
def test_get_sessionless_url_is_course_nav_fail(self, m):
Expand Down
2 changes: 1 addition & 1 deletion utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from collections import defaultdict
import json
import re
from collections import defaultdict

from canvasapi import Canvas

Expand Down