Repository: cylc/cylc-uiserver (language: python)

Issue:
travis => GH actions
Upgrade to GitHub actions.

**Pull requests welcome!**
<!-- This is an Open Source project - please consider contributing code yourself
(please read `CONTRIBUTING.md` before starting any work though). -->



Discussion / hints:

Fix patch:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 0fed5dec..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
----
-cache: pip
-dist: xenial
-addons:
-  apt:
-    update: true  # instruct travis-ci to always run apt-get before each build
-
-language: python
-python: 3.7
-
-before_install:
-  - pip install pycodestyle
-  - pycodestyle --exclude=".git,.tox,__pycache,venv,.eggs,build,jupyterhub_config.py,cylc/uiserver/websockets/"
-install:
-  - pip install .[all]
-script:
-  - python setup.py test
-after_success:
-  # Report metrics, such as coverage
-  - coverage xml --ignore-errors
-  - bash <(curl -s https://codecov.io/bash)
diff --git a/README.md b/README.md
index 1c56d514..b7d89366 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 [![Anaconda-Server Badge](https://anaconda.org/conda-forge/cylc-uiserver/badges/version.svg)](https://anaconda.org/conda-forge/cylc-uiserver)
 [![Anaconda-Server Badge](https://anaconda.org/conda-forge/cylc-uiserver/badges/downloads.svg)](https://anaconda.org/conda-forge/cylc-uiserver)
-[![Build Status](https://travis-ci.org/cylc/cylc-uiserver.svg?branch=master)](https://travis-ci.org/cylc/cylc-uiserver)
+[![Test](https://github.com/cylc/cylc-uiserver/actions/workflows/test.yml/badge.svg?branch=master&event=push)](https://github.com/cylc/cylc-uiserver/actions/workflows/test.yml)
 [![codecov](https://codecov.io/gh/cylc/cylc-uiserver/branch/master/graph/badge.svg)](https://codecov.io/gh/cylc/cylc-uiserver)
 
 # Cylc UI Server
diff --git a/cylc/uiserver/main.py b/cylc/uiserver/main.py
index 2716eac1..3a886218 100644
--- a/cylc/uiserver/main.py
+++ b/cylc/uiserver/main.py
@@ -27,7 +27,6 @@
 
 from tornado import web, ioloop
 from traitlets import (
-    HasTraits,
     Unicode,
     default,
 )
@@ -49,7 +48,9 @@
 )
 from cylc.uiserver.config import __file__ as CONFIG_FILE
 from .data_store_mgr import DataStoreMgr
-from .handlers import *
+from .handlers import (
+    MainHandler, StaticHandler, SubscriptionHandler, UIServerGraphQLHandler,
+    UserProfileHandler)
 from .resolvers import Resolvers
 from .websockets.tornado import TornadoSubscriptionServer
 from .workflows_mgr import WorkflowsManager
diff --git a/cylc/uiserver/scripts/hub.py b/cylc/uiserver/scripts/hub.py
index 6ba8f277..438d8cfb 100644
--- a/cylc/uiserver/scripts/hub.py
+++ b/cylc/uiserver/scripts/hub.py
@@ -18,8 +18,6 @@
 """
 
 import os
-from pathlib import Path
-
 from jupyterhub.app import main as hub_main
 
 from cylc.uiserver import __version__


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..9efabae2
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,49 @@
+name: Test
+
+on:
+  pull_request:
+  workflow_dispatch:
+  push:
+    branches: [master]
+
+jobs:
+  test:
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 15
+    strategy:
+      matrix:
+        os: ['ubuntu-latest']
+        python: ['3.7', '3.8', '3.9']
+        include:
+          - os: 'macos-latest'
+            python: '3.7'
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 2  # required by codecov
+
+      - name: Setup Python
+        uses: actions/setup-python@v2
+        with:
+          python-version: ${{ matrix.python }}
+
+      - name: Install tools
+        run: pip install flake8
+
+      - name: Style test
+        run: flake8 cylc/uiserver/ --extend-exclude="config_defaults.py,cylc/uiserver/websockets/"
+
+      - name: Install cylc-uiserver & dependencies
+        run: |
+          pip install git+https://github.com/cylc/cylc-flow@master
+          pip install .[all]
+
+      - name: Test
+        run: pytest --cov cylc/uiserver/tests
+
+      - name: Coverage
+        run: |
+          coverage xml --ignore-errors
+          bash <(curl -s https://codecov.io/bash)
diff --git a/cylc/uiserver/tests/conftest.py b/cylc/uiserver/tests/conftest.py
index 169946da..167ba6cf 100644
--- a/cylc/uiserver/tests/conftest.py
+++ b/cylc/uiserver/tests/conftest.py
@@ -19,8 +19,8 @@
 
 import pytest
 import zmq
-from cylc.flow.data_messages_pb2 import PbEntireWorkflow, PbWorkflow, \
-    PbFamilyProxy
+from cylc.flow.data_messages_pb2 import (
+    PbEntireWorkflow, PbWorkflow, PbFamilyProxy)
 from cylc.flow.network import ZMQSocketBase
 
 from cylc.uiserver.data_store_mgr import DataStoreMgr
diff --git a/cylc/uiserver/tests/test_handlers.py b/cylc/uiserver/tests/test_handlers.py
index bd7f8ecd..b5ede6a9 100644
--- a/cylc/uiserver/tests/test_handlers.py
+++ b/cylc/uiserver/tests/test_handlers.py
@@ -25,8 +25,9 @@
 from tornado.testing import AsyncHTTPTestCase, get_async_test_timeout
 from tornado.web import Application, HTTPError
 
-from cylc.uiserver.handlers import *
-from cylc.uiserver.main import *
+from cylc.uiserver.handlers import (
+    MainHandler, SubscriptionHandler, UserProfileHandler)
+from cylc.uiserver.main import MyApplication
 
 
 class MainHandlerTest(AsyncHTTPTestCase):
@@ -138,7 +139,7 @@ def test_websockets_check_origin_rejects_different_origin(self):
         handler = self._create_handler()
         host_header = 'ui.cylc'
         handler.request.headers['Host'] = host_header
-        assert not handler.check_origin(origin=f'http://ui.notcylc')
+        assert not handler.check_origin(origin='http://ui.notcylc')
 
     def test_websockets_context(self):
         handler = self._create_handler()
diff --git a/cylc/uiserver/tests/test_uiserver.py b/cylc/uiserver/tests/test_uiserver.py
index 8d40c45f..c89739c9 100644
--- a/cylc/uiserver/tests/test_uiserver.py
+++ b/cylc/uiserver/tests/test_uiserver.py
@@ -13,7 +13,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from cylc.uiserver.main import *
+from cylc.uiserver.main import CylcUIServer, MyApplication
 from tornado.web import StaticFileHandler, RequestHandler
 from graphene_tornado.tornado_graphql_handler import TornadoGraphQLHandler
 
@@ -28,24 +28,24 @@ def test_my_application():
 
 def test_cylcuiserver_absolute_path():
     """Test a Cylc UI server created with absolute path for static assets."""
-    cylc_uiserver = CylcUIServer(8000, '/static/path', '/users/test')
+    cylc_uiserver = CylcUIServer(8000, '/users/test', static='/static/path')
     assert cylc_uiserver._port == 8000
-    assert cylc_uiserver._static == '/static/path'
+    assert cylc_uiserver.ui_path == '/static/path'
     assert cylc_uiserver._jupyter_hub_service_prefix == '/users/test'
 
 
 def test_cylcuiserver_relative_path():
     """Test a Cylc UI server created with relative path for static assets."""
-    cylc_uiserver = CylcUIServer(8000, './', '/users/test')
+    cylc_uiserver = CylcUIServer(8000, '/users/test', static='./')
     assert cylc_uiserver._port == 8000
-    print(cylc_uiserver._static)
+    print(cylc_uiserver.ui_path)
     # the code is using the directory relative to the main script ATM
-    assert cylc_uiserver._static.endswith('cylc/uiserver')
+    assert cylc_uiserver.ui_path.endswith('cylc/uiserver')
     assert cylc_uiserver._jupyter_hub_service_prefix == '/users/test'
 
 
 def test_cylcuiserver_create_static_handler():
-    cylc_uiserver = CylcUIServer(8000, './static', '/prefix/')
+    cylc_uiserver = CylcUIServer(8000, '/prefix/', static='./static')
     handler = cylc_uiserver._create_static_handler('(imgs/*.png)')
     assert "/prefix/((imgs/*.png))" == handler[0]
     assert issubclass(handler[1], StaticFileHandler)
@@ -53,7 +53,7 @@ def test_cylcuiserver_create_static_handler():
 
 
 def test_cylcuiserver_create_handler():
-    cylc_uiserver = CylcUIServer(8000, './', '/prefix/')
+    cylc_uiserver = CylcUIServer(8000, '/prefix/', static='./')
     handler = cylc_uiserver._create_handler('tests', RequestHandler,
                                             schema=True, table=False,
                                             number=1)
@@ -65,7 +65,7 @@ def test_cylcuiserver_create_handler():
 
 
 def test_cylcuiserver_create_graphql_handler():
-    cylc_uiserver = CylcUIServer(8000, './', '/prefix/')
+    cylc_uiserver = CylcUIServer(8000, '/prefix/', static='./')
     handler = cylc_uiserver._create_graphql_handler(
         'tests', TornadoGraphQLHandler, graphiql=True)
     assert "/prefix/tests" == handler[0]
diff --git a/cylc/uiserver/tests/test_workflows_mgr.py b/cylc/uiserver/tests/test_workflows_mgr.py
index c932dcac..3a40a874 100644
--- a/cylc/uiserver/tests/test_workflows_mgr.py
+++ b/cylc/uiserver/tests/test_workflows_mgr.py
@@ -15,16 +15,20 @@
 
 from itertools import product
 from random import random
-
+from getpass import getuser
+import logging
 import pytest
-from cylc.flow.suite_files import (
-    SuiteFiles
-)
+from cylc.flow import ID_DELIM
+from cylc.flow.exceptions import ClientError, ClientTimeout
+from cylc.flow.network import API
+from cylc.flow.suite_files import SuiteFiles
 from pytest_mock import MockFixture
+import socket
 
 import cylc.uiserver.workflows_mgr as workflows_mgr_module
 from cylc.uiserver.main import CylcUIServer
-from cylc.uiserver.workflows_mgr import *
+from cylc.uiserver.workflows_mgr import (
+    CFF, est_workflow, workflow_request, WorkflowsManager)
 from .conftest import AsyncClientFixture
 
 
@@ -131,7 +135,7 @@ async def test_est_workflow(
 
     mocked_get_host_ip_by_name = mocker.patch(
         'cylc.uiserver.workflows_mgr.get_host_ip_by_name')
-    mocked_get_host_ip_by_name.side_effect = lambda x: f'remote_host' \
+    mocked_get_host_ip_by_name.side_effect = lambda x: 'remote_host' \
         if x == 'remote' else x
 
     r_reg, r_host, r_port, r_pub_port, r_client, r_result = await est_workflow(
@@ -376,32 +380,6 @@ async def test_unregister(
     assert workflow_name not in uiserver.workflows_mgr.inactive
 
 
-@pytest.mark.asyncio
-async def test_unregister(
-        uiserver: CylcUIServer,
-        one_workflow_aiter,
-        empty_aiter
-):
-    """A workflow, once registered, can be unregistered in the
-    workflow manager.
-
-    It will delegate to the data store to properly remove the
-    workflow from the data store attributes, and call the necessary
-    functions."""
-    workflow_name = 'unregister-me'
-
-    uiserver.workflows_mgr._scan_pipe = empty_aiter()
-    uiserver.workflows_mgr.inactive.add(workflow_name)
-    # NOTE: here we will yield a workflow that is not running, it does
-    #       not have the contact data and is inactive.
-    #       This is what forces the .update() to call unregister()!
-
-    await uiserver.workflows_mgr.update()
-
-    # now the workflow is not active, nor inactive, it is unregistered
-    assert workflow_name not in uiserver.workflows_mgr.inactive
-
-
 @pytest.mark.asyncio
 async def test_connect(
         uiserver: CylcUIServer,