Skip to content

Conversation

KatKatKateryna
Copy link
Collaborator

@KatKatKateryna KatKatKateryna commented Sep 5, 2025

Unit tests added to run PyQt plugin in a headless mode. This required some changes in the main code:

  • imports of qgis modules are made optional, as qgis is not required for pytests
  • commands requiring qgis types are also made optional (e.g. logging with QgsMessageLog)
  • all typing for qgis namespaces are "quoted" in case the code is running from pytests and the type is not imported

Additionally:

  • YML workflow is added to run unit tests on every push to a PR branch
  • small bug in Provider window (correct widgets' values are passed and read)

Screenshots:
unit_tests_yaml_2

Failed tests:
unit_tests_yaml_failed1
unit_tests_yaml_failed2

KatKatKateryna and others added 12 commits September 4, 2025 18:49
* adjust datetime format

* run tests on PR, show failed tests

* add pytests for file open-save

* typo

* separate the workflow

* absolute import

* stop ignoring yml on commit

* import top level repo

* run pytests from root

* run pytests from the workspace

* update imports in test folder

* init file for tests

* mode importlib

* import qgis as optional

* avoiding qgis imports if possible

* fix imports

* optional typing for qgis imports

* same

* make pyqt5 offscreen mode

* run tests in a virtual display

* Install PyQt5 dependencies manually

* switch to qbot instead of QApplication

* only init dialog

* remove typos

* YAML for windows

* runt test

* ignore Qgs imports

* run a bit more

* print URL

* print from start

* show print statements

* print statements flush

* more prints

* check file location

* more prints

* wrap QgsMessageLog to try/except

* print new file path for checking

* write wrong YAML

* Revert "write wrong YAML"

This reverts commit dd7a6e4.

* fix test

* remove datatime validation (for testing)

* Revert "remove datatime validation (for testing)"

This reverts commit 627f488.
@KatKatKateryna KatKatKateryna marked this pull request as ready for review September 5, 2025 13:13
@doublebyte1
Copy link
Contributor

👍🏽

Copy link
Contributor

@doublebyte1 doublebyte1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the unit tests, and for including them in the GitHub actions! 🥇

Could you also add a section about setting up and running the tests (standalone) in the README file?

I wrote some comments about reading the yaml test files directory from their repositories. Do you think that makes sense, or maybe there is a reason for having them here?

If we copy them here, I think that we may run into the risk that they become outdated, and we won't even know from which version they belong to. At the same time, reading them from the upstream repositories can also lead to failing tests, and it will be hard to pinpoint if it is because of code changes, or schema changes. I think one other option could also be to tag the config files with a commit (or release) hashtag, and in that way we would know exactly to which version they belong. I would ❤️ to hear your thoughts about this.

I think with all the code that you introduced, you are in a position to implement the most challenging test of all: comparing the input yaml and the output yaml 😺

I am aware this may require a refactoring of the code, but from a user's perspective, I think it is super important for the tool to not change anything that was not strictly updated by the user (even when these changes are apparently harmless).

import subprocess

from ..pygeoapi_config_dialog import PygeoapiConfigDialog

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a third test, to validate that the yaml that we input is the same as the yaml that we output:

Example:

diff cite.config.yml saved_cite.config.yml - The output of this command should be an empty set.

I foresee some challenges, in order to pass that text (but I am confident you can address them 😁 ):

  • The order of the keys may be different in the returned file; nevertheless that should still yield a valid result.
  • Some optional fields with default values are being added; that should not happen - we can delegate the task of adding default values to pygeoapi. For example this:
    hello-world:
    type: process
    processor:
      name: HelloWorld

Is being transformed into this:

    hello-world:
        type: collection
        title: ''
        description: ''
        keywords: []
        links: []
        extents:
            spatial:
                bbox: [-180, -90, 180, 90]
                crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
        providers: []

Copy link
Collaborator Author

@KatKatKateryna KatKatKateryna Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the issue here. The test is only writing YAML, while the normal user interaction would be to see the Error window and not be able to proceed with Save file option (the validation check is done before opening a window to choose Save file directory - that part is skipped in unit tests). This is solvable. But the mandatory fields would still be added on file opening, so I'll look for a way around to ignore them in diff.

Copy link
Contributor

@doublebyte1 doublebyte1 Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don´t think you should implement validation for this test. Maybe I should have picked an example of a collection type that is supported (not OGC API - Processes) (:

Let's take the lakes collection, at cite.config.yml:

    lakes:
        type: collection
        title: Large Lakes
        description: lakes of the world, public domain
        keywords:
            - lakes
        crs:
            - CRS84
        links:
            - type: text/html
              rel: canonical
              title: information
              href: http://www.naturalearthdata.com/
              hreflang: en-US
        extents:
            spatial:
                bbox: [-180,-90,180,90]
                crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
            temporal:
                begin: 2011-11-11T00:00:00Z
                end: null  # or empty
        providers:
            - type: tile
              name: MVT-tippecanoe
              data: ../data/tiles/ne_110m_lakes
              options:
                bounds: [[-124.953634,-16.536406],[109.929807,66.969298]]
                zoom:
                    min: 0
                    max: 5
              format:
                    name: pbf
                    mimetype: application/vnd.mapbox-vector-tile

The tests output this collection:

    lakes:
        type: collection
        title: Large Lakes
        description: lakes of the world, public domain
        keywords:
        - lakes
        links:
        -   type: text/html
            rel: canonical
            href: http://www.naturalearthdata.com/
            title: information
            hreflang: en-US
        extents:
            spatial:
                bbox: [-180, -90, 180, 90]
                crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
            temporal:
                begin: 2011-11-11T00:00:00Z
        providers:
        -   type: tile
            name: MVT-tippecanoe
            data: ../data/tiles/ne_110m_lakes
            options:
                layer: ''
                style: ''
                version: ''
            format:
                name: pbf
                mimetype: application/vnd.mapbox-vector-tile

The provider options had bounds and a zoom dictionary, but now I see a layer, style and version options that did not exist before, with empty values (maybe these come from WMSFacade?) I think this is the sort of thing that could be captured by a simple test that compares the input yaml and the output yaml (without ny validation).

Copy link
Contributor

@doublebyte1 doublebyte1 Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is happening to the OGC API - Processes collection points out a different issue. What happens to collections or providers that are not supported? I think they should be written in the output file without any changes, but some warning should be thrown to the user stating that a non supported key was detected and that we cannot guarantee the validation of that element. Having elements that are not supported breaks our ability to provide a valid pygeoapi config, but I guess there is not much we can do about that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The process collection is a bit different from the others; [the only required fields are 'type' and 'processor.name']. Type in this case is process, rather than collection.(https://github.com/geopython/pygeoapi/blob/5ed31b47773e7fc0b2a5b1a34055c3b2659d4952/pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml#L591).

A process is a mathematical function, which may not have a spatial extent. These fields do not apply in the case of processes:

    title: 
    description: 
    keywords:
    links:
    extents:
        spatial:
            bbox:
            crs:

If we cannot disable the validation, maybe it is better to updated the validator to look for these fields.

@KatKatKateryna
Copy link
Collaborator Author

Regarding the files to test against, I would keep them deterministic with the similar set of files to test against. With testing against the versioned config files, what would be that we want to test for exactly?

@doublebyte1
Copy link
Contributor

Regarding the files to test against, I would keep them deterministic with the similar set of files to test against. With testing against the versioned config files, what would be that we want to test for exactly?

Hopefully I have answered that question in my other comments. Let me know, if I didn't (:

@KatKatKateryna
Copy link
Collaborator Author

Thanks, all clear now! :)

@KatKatKateryna KatKatKateryna marked this pull request as draft October 6, 2025 01:04
@KatKatKateryna
Copy link
Collaborator Author

KatKatKateryna commented Oct 7, 2025

The DIFF files are significantly minimized, so there is no need to test each data type and provider type separately 🙏

  1. Unfortunately I cannot reproduce the issue with 'spatial.extents.CRS' jumping to default value - seems to be working fine. Could you please share the file you are opening, or the changes that you are trying to save? At which stage does the value returns to default?
  2. Resource specification does not seem to have 'crs', it is only for resource providers: https://github.com/geopython/pygeoapi/blob/5ed31b47773e7fc0b2a5b1a34055c3b2659d4952/pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml . I am not sure why the sample YAML files have this field

Last touches to complete:

  • do not force-add non-mandatory Server bools
  • support non-ISO temporal extents
  • add several more non-mandatory fields in different places (e.g. server.ogc_schemas_location, resource.linked-data)

@doublebyte1
Copy link
Contributor

  1. Unfortunately I cannot reproduce the issue with 'spatial.extents.CRS' jumping to default value - seems to be working fine. Could you please share the file you are opening, or the changes that you are trying to save? At which stage does the value returns to default?

I cannot reproduce it either, nevermind that! 😅

  1. Resource specification does not seem to have 'crs', it is only for resource providers: https://github.com/geopython/pygeoapi/blob/5ed31b47773e7fc0b2a5b1a34055c3b2659d4952/pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml . I am not sure why the sample YAML files have this field

You are right, but I am a bit puzzled that the collection level metadata crs is present at the configuration files for the demo cite instance:

https://github.com/geopython/demo.pygeoapi.io/blob/e10f3f2e85ab91cb7a4196deeb227efa030f3477/services/pygeoapi_cite/pygeoapi/cite.config.yml#L67

Let me dig a bit in the code and come back to you, on this one. 👍🏽

Last touches to complete:

  • do not force-add non-mandatory Server bools
  • support non-ISO temporal extents
  • add several more non-mandatory fields in different places (e.g. server.ogc_schemas_location, resource.linked-data)

@doublebyte1
Copy link
Contributor

As we discussed, here are the keys that should have free formed strings in the UI (to allow environmental variables):

  • server.url
  • server.bind.host
  • server.bind.port
  • On providers.feature.postgresql: data.host, data.port, data.dbname, data.user, data.password

There are examples in the reference file:
https://github.com/byteroad/pygeoapi-config/blob/a015a48e1f7328cff9e070f9a59b764c5bbac2c0/docker.config.yml#L5
https://github.com/byteroad/pygeoapi-config/blob/a015a48e1f7328cff9e070f9a59b764c5bbac2c0/docker.config.yml#L210C1-L215C53

@KatKatKateryna KatKatKateryna marked this pull request as ready for review October 9, 2025 00:34
@KatKatKateryna
Copy link
Collaborator Author

Applied all the changes we discussed today and moved problematic YAML files out of the 'samples' folder so they won't block the tests 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants