Files
proxy/nodejs/test
wmantly 1c7ad9aaae Fix DuckDNS domains field colliding with DnsProvider's own relation (#127)
Reported error when adding a DuckDNS provider:

  TypeError: this.domains.map is not a function
    at Proxy.updateDomains (models/dns_provider.js:185:37)

DnsProvider.__intraModel merges `{...DnsProvider._keyMap,
...Provider._keyMap}`, so a provider-defined field with the same name
as one of DnsProvider's own (created_by, updated_by, name,
dnsProvider, domains, id) silently overwrites it. DuckDNS defined a
`domains` field (the operator-supplied comma-separated subdomain
list), which replaced DnsProvider's `domains` relation (rel: 'many' to
Domain, populated by updateDomains()) — so `this.domains` stopped
being the array relation and became DuckDNS's raw string instead.

Rename the field to `subdomains` throughout (model, docs, tests). Add
a comment on __intraModel documenting the collision risk for future
providers, and a regression test asserting no registered provider's
_keyMap redefines one of DnsProvider's reserved field names.
2026-07-14 01:24:53 -04:00
..

Test Suite

This project uses Node.js built-in test runner (requires Node 18+). No external testing dependencies required.

Running Tests

# Run all tests
npm test

# Run only unit tests
npm run test:unit

# Run only integration tests
npm run test:integration

# Run tests in watch mode (auto-rerun on file changes)
npm run test:watch

Test Structure

test/
├── unit/              # Unit tests for isolated components
│   ├── basicauth.test.js
│   ├── callback_queue.test.js
│   ├── dynamic_record.test.js
│   ├── host_features.test.js
│   ├── host_lookup.test.js
│   ├── hostname_validate.test.js
│   ├── host_sso.test.js
│   ├── oidc.test.js
│   ├── password_policy.test.js
│   ├── roles.test.js
│   ├── safe_redirect.test.js
│   ├── unix_socket.test.js
│   └── wildcard_matchany.test.js
├── integration/       # Integration tests for complex interactions
│   └── dns_provider.test.js
└── helpers/           # Test utilities and contracts
    └── dns_provider_contract.js

What We Test

Unit Tests

callback_queue.test.js

  • Callback registration and invocation
  • Multiple callbacks with arguments
  • Error handling

host_lookup.test.js

  • Host lookup tree algorithm
  • Wildcard matching (single and double)
  • Exact match priority
  • Edge cases (no match, empty input, etc.)

unix_socket.test.js

  • Unix socket server creation
  • JSON message parsing
  • Partial data buffering
  • Multiple connections
  • Error handling

Integration Tests

dns_provider.test.js

  • DNS provider contract compliance
  • All existing providers (Cloudflare, DigitalOcean, PorkBun, DuckDNS)
  • Method signatures
  • Key mapping
  • Type validation

Adding a New DNS Provider

When you add a new DNS provider, you MUST add tests to ensure it meets the contract:

  1. Create your provider class extending DnsApi in models/dns_provider/yourprovider.js

  2. Add a test block in test/integration/dns_provider.test.js:

describe('YourProvider Provider', () => {
    const YourProvider = require('../../models/dns_provider/yourprovider');

    test('should meet DNS provider contract', () => {
        const mockCredentials = {api_key: 'mock-key'};
        const instance = validateDnsProviderContract(YourProvider, mockCredentials);
        assert.ok(instance, 'YourProvider should be instantiated');
    });

    test('should have correct _keyMap structure', () => {
        // Test your specific credential requirements
        assert.ok(YourProvider._keyMap.api_key);
        assert.strictEqual(YourProvider._keyMap.api_key.type, 'string');
        assert.strictEqual(YourProvider._keyMap.api_key.isRequired, true);
    });

    test('should have valid method signatures', () => {
        const instance = new YourProvider({api_key: 'mock'});
        validateMethodSignatures(instance);
    });

    test('should validate key mapping', () => {
        const instance = new YourProvider({api_key: 'mock'});
        validateKeyMapping(instance);
    });

    test('should validate type checking', () => {
        const instance = new YourProvider({api_key: 'mock'});
        validateTypeChecking(instance);
    });
});
  1. Run tests to verify compliance:
npm run test:integration

DNS Provider Contract

All DNS providers must:

  1. Extend DnsApi base class
  2. Define static _keyMap with required credentials
  3. Define static display properties: displayName, displayIconHtml, displayIconUni
  4. Implement required methods:
    • listDomains() - Returns array of {domain, zoneId}
    • getRecords(domain, options) - Returns array of DNS records
    • createRecord(domain, options) - Creates a record
    • deleteRecords(domain, options) - Deletes matching records
  5. Define __apiKeyMap to translate between class keys and API keys
  6. Implement or inherit __typeCheck() for record type validation
  7. Throw appropriate errors from this.errors object

CI/CD Integration

Tests can be run in GitHub Actions, GitLab CI, or any CI/CD system:

# Example GitHub Actions workflow
- name: Run tests
  run: npm test

Philosophy

We test custom logic, not third-party code:

  • YES: Test our host lookup algorithm
  • YES: Test our socket buffering logic
  • YES: Test DNS provider contracts
  • NO: Don't test Express.js routing
  • NO: Don't test the Redis ORM
  • NO: Don't test external DNS APIs (use mocks)

Notes

  • Tests use Node's built-in node:test and node:assert modules
  • No external testing framework needed
  • Tests are fast and run in parallel by default
  • Mock external services (Redis, DNS APIs) to avoid network calls
  • Focus on testing business logic, not infrastructure