Skip to content
Open
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
126 changes: 126 additions & 0 deletions backend/tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Backend Tests

This directory contains Django tests for the backend app, specifically for testing the backend configuration API.

## Structure

- `test_api_config.py` - Main test file for the backend configuration API endpoints
- `utils.py` - Utility functions for loading test fixtures and creating test data
- `fixtures/` - Directory containing test fixture files with sample configurations

## Test Files

### `test_api_config.py`
Contains comprehensive tests for the `/api/config/<backend-slug>` endpoint:

- GET requests (no authentication required)
- POST requests (authentication required)
- Error handling (invalid YAML, missing fields, etc.)
- Backend creation and updating
- Examples management

### `utils.py`
Utility functions for tests:

- `load_test_fixture(filename)` - Load fixture files
- `load_invalid_config_example(example_name)` - Load specific invalid config examples
- `create_test_backend_config()` - Generate test configurations programmatically
- `create_test_example()` - Create test example queries

## Fixtures

### `fixtures/sample_backend_config.yaml`
Complete backend configuration with all available options, including:
- Complex SPARQL queries for suggestions
- Multiple example queries
- Full configuration options

### `fixtures/minimal_backend_config.yaml`
Minimal valid backend configuration for basic testing.

### `fixtures/wikidata_example_config.yaml`
Realistic Wikidata-based configuration with:
- Wikidata-specific prefixes
- Real-world SPARQL queries
- Service-based label resolution

### `fixtures/invalid_config_examples.yaml`
Collection of invalid configurations for testing error handling:
- Missing required fields
- Invalid YAML syntax
- Type mismatches
- Structural errors

## Running Tests

### Run all backend tests:
```bash
python manage.py test backend.tests
```

### Run specific test class:
```bash
python manage.py test backend.tests.test_api_config.BackendConfigAPITestCase
```

### Run specific test method:
```bash
python manage.py test backend.tests.test_api_config.BackendConfigAPITestCase.test_post_config_create_new_backend
```

### Run with verbose output:
```bash
python manage.py test backend.tests -v 2
```

## Test Coverage

The tests cover:

1. **Authentication and Authorization**
- GET requests work without authentication
- POST requests require authentication
- Proper 403 responses for unauthenticated POST requests

2. **Backend Creation**
- Creating new backends via POST
- Auto-creation of minimal backend if it doesn't exist
- Proper validation of required fields

3. **Backend Updates**
- Updating existing backends via POST
- Overwriting existing examples
- Preserving backend ID during updates

4. **Error Handling**
- Invalid YAML syntax
- Missing required fields
- Slug mismatches between URL and YAML
- Empty request bodies
- Unsupported HTTP methods (PUT, DELETE)

5. **Data Integrity**
- Proper creation of Backend and Example objects
- Correct field mappings from YAML to database
- Round-trip testing (POST then GET)

6. **Response Formats**
- JSON responses for POST requests
- YAML responses for GET requests
- Proper HTTP status codes
- Informative error messages

## Adding New Tests

When adding new tests:

1. Follow the existing naming convention (`test_<functionality>`)
2. Include comprehensive docstrings
3. Use the utility functions for creating test data
4. Add new fixture files for complex test scenarios
5. Test both success and error cases
6. Verify database state changes where applicable

## Test Data Cleanup

Django's TestCase automatically handles database cleanup between tests, so no manual cleanup is required. Test databases are created and destroyed for each test run.
1 change: 1 addition & 0 deletions backend/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Django tests for backend app
77 changes: 77 additions & 0 deletions backend/tests/fixtures/invalid_config_examples.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# This file contains various invalid YAML configurations for testing error handling

# Missing required fields
missing_name: |
config:
backend:
slug: test-missing-name
baseUrl: https://example.com
examples: []

# Invalid YAML syntax
invalid_yaml_syntax: |
config:
backend:
name: "Unclosed quote
slug: invalid-yaml
baseUrl: https://example.com
examples: []

# Missing config key
missing_config_key: |
backend:
name: Missing Config Key
slug: missing-config
baseUrl: https://example.com
examples: []

# Invalid boolean value
invalid_boolean: |
config:
backend:
name: Invalid Boolean
slug: invalid-bool
baseUrl: https://example.com
isDefault: "maybe"
examples: []

# Slug mismatch
slug_mismatch: |
config:
backend:
name: Slug Mismatch Test
slug: different-slug-than-url
baseUrl: https://example.com
examples: []

# Missing examples key
missing_examples: |
config:
backend:
name: Missing Examples
slug: missing-examples
baseUrl: https://example.com

# Invalid example structure
invalid_example_structure: |
config:
backend:
name: Invalid Example Structure
slug: invalid-example
baseUrl: https://example.com
examples:
- name: "Missing sort_key and query"
- sort_key: "1"
query: "Missing name"
- name: "Missing query"
sort_key: "2"

# Non-string values where strings expected
non_string_values: |
config:
backend:
name: 12345 # Should be string but is number
slug: non-string-test
baseUrl: https://example.com
maxDefault: "not a number" # Should be int but is string
examples: []
11 changes: 11 additions & 0 deletions backend/tests/fixtures/minimal_backend_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
config:
backend:
name: Minimal Test Backend
slug: minimal-test
baseUrl: https://minimal.example.com/sparql
isDefault: false
isNoSlugMode: false
examples:
- name: Simple SELECT
sort_key: "1"
query: "SELECT * WHERE { ?s ?p ?o } LIMIT 5"
105 changes: 105 additions & 0 deletions backend/tests/fixtures/sample_backend_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
config:
backend:
name: Sample Backend
slug: sample-backend
baseUrl: https://example.com/sparql
apiToken: sample-token-123
isDefault: false
isNoSlugMode: false
maxDefault: 100
filteredLanguage: en,de,fr
dynamicSuggestions: 2
defaultModeTimeout: 1.5
mixedModeTimeout: 0.5
suggestSubjects: |
SELECT DISTINCT ?qleverui_entity ?qleverui_name WHERE {
?qleverui_entity rdfs:label ?qleverui_name .
FILTER(LANG(?qleverui_name) = "en")
} ORDER BY ?qleverui_name LIMIT 20
suggestPredicates: |
SELECT DISTINCT ?qleverui_entity ?qleverui_name WHERE {
[] ?qleverui_entity [] .
OPTIONAL { ?qleverui_entity rdfs:label ?qleverui_name }
} ORDER BY ?qleverui_name LIMIT 20
suggestObjects: |
SELECT DISTINCT ?qleverui_entity ?qleverui_name WHERE {
[] [] ?qleverui_entity .
OPTIONAL { ?qleverui_entity rdfs:label ?qleverui_name }
} ORDER BY ?qleverui_name LIMIT 20
subjectName: |
?qleverui_entity rdfs:label ?qleverui_name .
FILTER(LANG(?qleverui_name) = "en")
predicateName: |
?qleverui_entity rdfs:label ?qleverui_name .
FILTER(LANG(?qleverui_name) = "en")
objectName: |
?qleverui_entity rdfs:label ?qleverui_name .
FILTER(LANG(?qleverui_name) = "en")
supportedKeywords: select, where, filter, optional, union, minus, limit, offset, order, group, having, distinct, reduced, construct, describe, ask
supportedFunctions: str, lang, datatype, bound, isiri, isblank, isliteral, isnumeric, regex, strlen, substr, ucase, lcase, concat, contains, strstarts, strends, strbefore, strafter
supportedPredicateSuggestions: rdfs:label, rdf:type, rdfs:comment
suggestPrefixnamesForPredicates: true
fillPrefixes: true
filterEntities: false
suggestedPrefixes: |
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
suggestionEntityVariable: entity
suggestionNameVariable: name
suggestionAltNameVariable: altname
suggestionReversedVariable: reversed
examples:
- name: Basic Query
sort_key: "01"
query: |
SELECT ?subject ?predicate ?object WHERE {
?subject ?predicate ?object .
} LIMIT 10
- name: Find All Classes
sort_key: "02"
query: |
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT DISTINCT ?class ?label WHERE {
?class rdf:type rdfs:Class .
OPTIONAL { ?class rdfs:label ?label }
} ORDER BY ?label LIMIT 50
- name: Count Entities by Type
sort_key: "03"
query: |
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

SELECT ?type (COUNT(?entity) as ?count) WHERE {
?entity rdf:type ?type .
} GROUP BY ?type
ORDER BY DESC(?count)
LIMIT 20
- name: Find Properties with Labels
sort_key: "04"
query: |
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT ?property ?label WHERE {
?property rdf:type rdf:Property .
OPTIONAL { ?property rdfs:label ?label }
} ORDER BY ?label LIMIT 30
- name: Complex Query with OPTIONAL and FILTER
sort_key: "05"
query: |
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

SELECT ?person ?name ?email WHERE {
?person rdf:type foaf:Person .
?person foaf:name ?name .
OPTIONAL { ?person foaf:email ?email }
FILTER(LANG(?name) = "en" || LANG(?name) = "")
} ORDER BY ?name LIMIT 25
Loading