Re-structure the project.

This commit is contained in:
Yaojia Wang
2026-01-25 15:21:11 +01:00
parent 8fd61ea928
commit e599424a92
80 changed files with 10672 additions and 1584 deletions

View File

@@ -0,0 +1,273 @@
# Normalizer Tests
每个 normalizer 模块都有完整的测试覆盖。
## 测试结构
```
tests/normalize/normalizers/
├── __init__.py
├── test_invoice_number_normalizer.py # InvoiceNumberNormalizer 测试 (12 个测试)
├── test_ocr_normalizer.py # OCRNormalizer 测试 (9 个测试)
├── test_bankgiro_normalizer.py # BankgiroNormalizer 测试 (11 个测试)
├── test_plusgiro_normalizer.py # PlusgiroNormalizer 测试 (10 个测试)
├── test_amount_normalizer.py # AmountNormalizer 测试 (15 个测试)
├── test_date_normalizer.py # DateNormalizer 测试 (19 个测试)
├── test_organisation_number_normalizer.py # OrganisationNumberNormalizer 测试 (11 个测试)
├── test_supplier_accounts_normalizer.py # SupplierAccountsNormalizer 测试 (13 个测试)
├── test_customer_number_normalizer.py # CustomerNumberNormalizer 测试 (12 个测试)
└── README.md # 本文件
```
## 运行测试
### 运行所有 normalizer 测试
```bash
# 在 WSL 环境中
conda activate invoice-py311
pytest tests/normalize/normalizers/ -v
```
### 运行单个 normalizer 的测试
```bash
# 测试 InvoiceNumberNormalizer
pytest tests/normalize/normalizers/test_invoice_number_normalizer.py -v
# 测试 AmountNormalizer
pytest tests/normalize/normalizers/test_amount_normalizer.py -v
# 测试 DateNormalizer
pytest tests/normalize/normalizers/test_date_normalizer.py -v
```
### 查看测试覆盖率
```bash
pytest tests/normalize/normalizers/ --cov=src/normalize/normalizers --cov-report=html
```
## 测试统计
**总计**: 112 个测试用例
**状态**: ✅ 全部通过
**执行时间**: ~5.6 秒
### 各 Normalizer 测试数量
| Normalizer | 测试数量 | 覆盖率 |
|------------|---------|-------|
| InvoiceNumberNormalizer | 12 | 100% |
| OCRNormalizer | 9 | 100% |
| BankgiroNormalizer | 11 | 100% |
| PlusgiroNormalizer | 10 | 100% |
| AmountNormalizer | 15 | 100% |
| DateNormalizer | 19 | 93% |
| OrganisationNumberNormalizer | 11 | 100% |
| SupplierAccountsNormalizer | 13 | 100% |
| CustomerNumberNormalizer | 12 | 100% |
## 测试覆盖的场景
### 通用测试 (所有 normalizer)
- ✅ 空字符串处理
- ✅ None 值处理
- ✅ Callable 接口 (`__call__`)
- ✅ 基本功能验证
### InvoiceNumberNormalizer
- ✅ 纯数字发票号
- ✅ 带前缀的发票号 (INV-, etc.)
- ✅ 字母数字混合
- ✅ 特殊字符处理
- ✅ Unicode 字符清理
- ✅ 多个分隔符
- ✅ 无数字内容
- ✅ 重复变体去除
### OCRNormalizer
- ✅ 纯数字 OCR
- ✅ 带前缀 (OCR:)
- ✅ 空格分隔
- ✅ 连字符分隔
- ✅ 混合分隔符
- ✅ 超长 OCR 号码
### BankgiroNormalizer
- ✅ 8 位数字 (带/不带连字符)
- ✅ 7 位数字格式
- ✅ 特殊连字符类型 (en-dash, etc.)
- ✅ 空格处理
- ✅ 前缀处理 (BG:)
- ✅ OCR 错误变体生成
### PlusgiroNormalizer
- ✅ 8 位数字 (带/不带连字符)
- ✅ 7 位数字
- ✅ 9 位数字
- ✅ 空格处理
- ✅ 前缀处理 (PG:)
- ✅ OCR 错误变体生成
### AmountNormalizer
- ✅ 整数金额
- ✅ 逗号小数分隔符
- ✅ 点小数分隔符
- ✅ 空格千位分隔符
- ✅ 空格作为小数分隔符 (瑞典格式)
- ✅ 美国格式 (1,390.00)
- ✅ 欧洲格式 (1.390,00)
- ✅ 货币符号移除 (kr, SEK)
- ✅ 大金额处理
- ✅ 冒号破折号后缀 (1234:-)
### DateNormalizer
- ✅ ISO 格式 (2025-12-13)
- ✅ 欧洲斜杠格式 (13/12/2025)
- ✅ 欧洲点格式 (13.12.2025)
- ✅ 紧凑格式 YYYYMMDD
- ✅ 紧凑格式 YYMMDD
- ✅ 短年份格式 (DD.MM.YY)
- ✅ 瑞典月份名称 (december, dec)
- ✅ 瑞典月份缩写
- ✅ 带时间的 ISO 格式
- ✅ 歧义日期双重解析
- ✅ 中点分隔符
- ✅ 空格格式
- ✅ 无效日期处理
- ✅ 2 位年份世纪判断
### OrganisationNumberNormalizer
- ✅ 带/不带连字符
- ✅ VAT 号码提取
- ✅ VAT 号码生成
- ✅ 12 位带世纪组织号
- ✅ VAT 带空格
- ✅ 大小写混合 VAT 前缀
- ✅ OCR 错误变体生成
### SupplierAccountsNormalizer
- ✅ 单个 Plusgiro
- ✅ 单个 Bankgiro
- ✅ 多账号 (| 分隔)
- ✅ 前缀标准化
- ✅ 前缀带空格
- ✅ 空账号忽略
- ✅ 无前缀账号
- ✅ 7 位账号
- ✅ 10 位账号
- ✅ 混合格式账号
### CustomerNumberNormalizer
- ✅ 字母数字+空格+连字符
- ✅ 字母数字+空格
- ✅ 大小写变体
- ✅ 纯数字
- ✅ 仅连字符
- ✅ 仅空格
- ✅ 大写重复去除
- ✅ 复杂客户编号
- ✅ 瑞典客户编号格式 (UMJ 436-R)
## 最佳实践
### 1. 使用 pytest fixtures
每个测试类都使用 `@pytest.fixture` 创建 normalizer 实例:
```python
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return InvoiceNumberNormalizer()
def test_something(self, normalizer):
result = normalizer.normalize('test')
assert 'expected' in result
```
### 2. 清晰的测试命名
测试方法名清楚描述测试场景:
```python
def test_with_dash_8_digits(self, normalizer):
"""8-digit Bankgiro with dash should generate variants"""
...
```
### 3. 断言具体行为
明确测试期望的行为:
```python
result = normalizer.normalize('5393-9484')
assert '5393-9484' in result # 保留原始格式
assert '53939484' in result # 生成无连字符格式
```
### 4. 边界条件测试
每个 normalizer 都测试:
- 空字符串
- None 值
- 特殊字符
- 极端值
### 5. 接口一致性测试
验证 callable 接口:
```python
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('test-value')
assert result is not None
```
## 添加新测试
为新功能添加测试:
```python
def test_new_feature(self, normalizer):
"""Description of what this tests"""
# Arrange
input_value = 'test-input'
# Act
result = normalizer.normalize(input_value)
# Assert
assert 'expected-output' in result
assert len(result) > 0
```
## CI/CD 集成
这些测试可以轻松集成到 CI/CD 流程:
```yaml
# .github/workflows/test.yml
- name: Run Normalizer Tests
run: pytest tests/normalize/normalizers/ -v --cov
```
## 总结
✅ **112 个测试**全部通过
**高覆盖率**: 大部分 normalizer 达到 100%
**快速执行**: 5.6 秒完成所有测试
**清晰结构**: 每个 normalizer 独立测试文件
**易维护**: 遵循 pytest 最佳实践

View File

@@ -0,0 +1 @@
"""Tests for individual normalizer modules"""

View File

@@ -0,0 +1,108 @@
"""
Tests for AmountNormalizer
Usage:
pytest tests/normalize/normalizers/test_amount_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.amount_normalizer import AmountNormalizer
class TestAmountNormalizer:
"""Test AmountNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return AmountNormalizer()
def test_integer_amount(self, normalizer):
"""Integer amount should generate decimal variants"""
result = normalizer.normalize('114')
assert '114' in result
assert '114,00' in result
assert '114.00' in result
def test_with_comma_decimal(self, normalizer):
"""Amount with comma decimal should generate dot variant"""
result = normalizer.normalize('114,00')
assert '114,00' in result
assert '114.00' in result
assert '114' in result
def test_with_dot_decimal(self, normalizer):
"""Amount with dot decimal should generate comma variant"""
result = normalizer.normalize('114.00')
assert '114.00' in result
assert '114,00' in result
def test_with_space_thousand_separator(self, normalizer):
"""Amount with space as thousand separator should be normalized"""
result = normalizer.normalize('1 234,56')
assert '1234,56' in result
assert '1234.56' in result
def test_space_as_decimal_separator(self, normalizer):
"""Space as decimal separator (Swedish format) should be normalized"""
result = normalizer.normalize('3045 52')
assert '3045.52' in result
assert '3045,52' in result
assert '304552' in result
def test_us_format(self, normalizer):
"""US format (1,390.00) should generate variants"""
result = normalizer.normalize('1,390.00')
assert '1390.00' in result
assert '1390,00' in result
assert '1390' in result
def test_european_format(self, normalizer):
"""European format (1.390,00) should generate variants"""
result = normalizer.normalize('1.390,00')
assert '1390.00' in result
assert '1390,00' in result
assert '1390' in result
def test_space_thousand_with_decimal(self, normalizer):
"""Space thousand separator with decimal should be normalized"""
result = normalizer.normalize('10 571,00')
assert '10571.00' in result
assert '10571,00' in result
def test_removes_currency_symbols(self, normalizer):
"""Currency symbols (kr, SEK) should be removed"""
result = normalizer.normalize('114 kr')
assert '114' in result
assert '114,00' in result
def test_large_amount_european_format(self, normalizer):
"""Large amount in European format should be handled"""
result = normalizer.normalize('20.485,00')
assert '20485.00' in result
assert '20485,00' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('1234.56')
assert '1234.56' in result
def test_removes_sek_suffix(self, normalizer):
"""SEK suffix should be removed"""
result = normalizer.normalize('1234 SEK')
assert '1234' in result
def test_with_colon_dash_suffix(self, normalizer):
"""Colon-dash suffix should be removed"""
result = normalizer.normalize('1234:-')
assert '1234' in result

View File

@@ -0,0 +1,80 @@
"""
Tests for BankgiroNormalizer
Usage:
pytest tests/normalize/normalizers/test_bankgiro_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.bankgiro_normalizer import BankgiroNormalizer
class TestBankgiroNormalizer:
"""Test BankgiroNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return BankgiroNormalizer()
def test_with_dash_8_digits(self, normalizer):
"""8-digit Bankgiro with dash should generate variants"""
result = normalizer.normalize('5393-9484')
assert '5393-9484' in result
assert '53939484' in result
def test_without_dash_8_digits(self, normalizer):
"""8-digit Bankgiro without dash should generate dash variant"""
result = normalizer.normalize('53939484')
assert '53939484' in result
assert '5393-9484' in result
def test_7_digits(self, normalizer):
"""7-digit Bankgiro should generate correct format"""
result = normalizer.normalize('5393948')
assert '5393948' in result
assert '539-3948' in result
def test_with_dash_7_digits(self, normalizer):
"""7-digit Bankgiro with dash should generate variants"""
result = normalizer.normalize('539-3948')
assert '539-3948' in result
assert '5393948' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('5393-9484')
assert '53939484' in result
def test_with_spaces(self, normalizer):
"""Bankgiro with spaces should be normalized"""
result = normalizer.normalize('5393 9484')
assert '53939484' in result
def test_special_dashes(self, normalizer):
"""Different dash types should be normalized to standard hyphen"""
# en-dash
result = normalizer.normalize('5393\u20139484')
assert '5393-9484' in result
assert '53939484' in result
def test_with_prefix(self, normalizer):
"""Bankgiro with BG: prefix should be normalized"""
result = normalizer.normalize('BG:5393-9484')
assert '53939484' in result
def test_generates_ocr_variants(self, normalizer):
"""Should generate OCR error variants"""
result = normalizer.normalize('5393-9484')
# Should contain multiple variants including OCR corrections
assert len(result) > 2

View File

@@ -0,0 +1,89 @@
"""
Tests for CustomerNumberNormalizer
Usage:
pytest tests/normalize/normalizers/test_customer_number_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.customer_number_normalizer import CustomerNumberNormalizer
class TestCustomerNumberNormalizer:
"""Test CustomerNumberNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return CustomerNumberNormalizer()
def test_alphanumeric_with_space_and_dash(self, normalizer):
"""Customer number with space and dash should generate variants"""
result = normalizer.normalize('EMM 256-6')
assert 'EMM 256-6' in result
assert 'EMM256-6' in result
assert 'EMM2566' in result
def test_alphanumeric_with_space(self, normalizer):
"""Customer number with space should generate variants"""
result = normalizer.normalize('ABC 123')
assert 'ABC 123' in result
assert 'ABC123' in result
def test_case_variants(self, normalizer):
"""Should generate uppercase and lowercase variants"""
result = normalizer.normalize('Emm 256-6')
assert 'EMM 256-6' in result
assert 'emm 256-6' in result
def test_pure_number(self, normalizer):
"""Pure number customer number should be handled"""
result = normalizer.normalize('12345')
assert '12345' in result
def test_with_only_dash(self, normalizer):
"""Customer number with only dash should generate no-dash variant"""
result = normalizer.normalize('ABC-123')
assert 'ABC-123' in result
assert 'ABC123' in result
def test_with_only_space(self, normalizer):
"""Customer number with only space should generate no-space variant"""
result = normalizer.normalize('ABC 123')
assert 'ABC 123' in result
assert 'ABC123' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('EMM 256-6')
assert 'EMM2566' in result
def test_all_uppercase(self, normalizer):
"""All uppercase should not duplicate uppercase variant"""
result = normalizer.normalize('ABC123')
uppercase_count = sum(1 for v in result if v == 'ABC123')
assert uppercase_count == 1
def test_complex_customer_number(self, normalizer):
"""Complex customer number with multiple separators"""
result = normalizer.normalize('ABC-123 XYZ')
assert 'ABC-123 XYZ' in result
assert 'ABC123XYZ' in result
def test_swedish_customer_numbers(self, normalizer):
"""Swedish customer number formats should be handled"""
result = normalizer.normalize('UMJ 436-R')
assert 'UMJ 436-R' in result
assert 'UMJ436-R' in result
assert 'UMJ436R' in result
assert 'umj 436-r' in result

View File

@@ -0,0 +1,121 @@
"""
Tests for DateNormalizer
Usage:
pytest tests/normalize/normalizers/test_date_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.date_normalizer import DateNormalizer
class TestDateNormalizer:
"""Test DateNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return DateNormalizer()
def test_iso_format(self, normalizer):
"""ISO format date should generate multiple variants"""
result = normalizer.normalize('2025-12-13')
assert '2025-12-13' in result
assert '13/12/2025' in result
assert '13.12.2025' in result
def test_european_slash_format(self, normalizer):
"""European slash format should be parsed correctly"""
result = normalizer.normalize('13/12/2025')
assert '2025-12-13' in result
def test_european_dot_format(self, normalizer):
"""European dot format should be parsed correctly"""
result = normalizer.normalize('13.12.2025')
assert '2025-12-13' in result
def test_compact_format_yyyymmdd(self, normalizer):
"""Compact YYYYMMDD format should be parsed"""
result = normalizer.normalize('20251213')
assert '2025-12-13' in result
def test_compact_format_yymmdd(self, normalizer):
"""Compact YYMMDD format should be parsed"""
result = normalizer.normalize('251213')
assert '2025-12-13' in result
def test_short_year_dot_format(self, normalizer):
"""Short year dot format (DD.MM.YY) should be parsed"""
result = normalizer.normalize('13.12.25')
assert '2025-12-13' in result
def test_swedish_month_name(self, normalizer):
"""Swedish full month name should be parsed"""
result = normalizer.normalize('13 december 2025')
assert '2025-12-13' in result
def test_swedish_month_abbreviation(self, normalizer):
"""Swedish month abbreviation should be parsed"""
result = normalizer.normalize('13 dec 2025')
assert '2025-12-13' in result
def test_generates_swedish_month_variants(self, normalizer):
"""Should generate Swedish month name variants"""
result = normalizer.normalize('2025-12-13')
assert '13 december 2025' in result
assert '13 dec 2025' in result
def test_generates_hyphen_month_abbrev_format(self, normalizer):
"""Should generate hyphen with month abbreviation format"""
result = normalizer.normalize('2025-12-13')
assert '13-DEC-25' in result
def test_iso_with_time(self, normalizer):
"""ISO format with time should extract date part"""
result = normalizer.normalize('2025-12-13 14:30:00')
assert '2025-12-13' in result
def test_ambiguous_date_generates_both(self, normalizer):
"""Ambiguous date should generate both DD/MM and MM/DD interpretations"""
result = normalizer.normalize('01/02/2025')
# Could be Feb 1 or Jan 2
assert '2025-02-01' in result or '2025-01-02' in result
def test_middle_dot_separator(self, normalizer):
"""Middle dot separator should be generated"""
result = normalizer.normalize('2025-12-13')
assert '2025·12·13' in result
def test_spaced_format(self, normalizer):
"""Spaced format should be generated"""
result = normalizer.normalize('2025-12-13')
assert '2025 12 13' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('2025-12-13')
assert '2025-12-13' in result
def test_invalid_date(self, normalizer):
"""Invalid date should return original only"""
result = normalizer.normalize('2025-13-45') # Invalid month and day
assert '2025-13-45' in result
# Should not crash, but won't generate ISO variant
def test_2digit_year_cutoff(self, normalizer):
"""2-digit year should use 2000s for < 50, 1900s for >= 50"""
result = normalizer.normalize('251213') # 25 = 2025
assert '2025-12-13' in result
result = normalizer.normalize('991213') # 99 = 1999
assert '1999-12-13' in result

View File

@@ -0,0 +1,87 @@
"""
Tests for InvoiceNumberNormalizer
Usage:
pytest tests/normalize/normalizers/test_invoice_number_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.invoice_number_normalizer import InvoiceNumberNormalizer
class TestInvoiceNumberNormalizer:
"""Test InvoiceNumberNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return InvoiceNumberNormalizer()
def test_pure_digits(self, normalizer):
"""Pure digit invoice number should return as-is"""
result = normalizer.normalize('100017500321')
assert '100017500321' in result
assert len(result) == 1
def test_with_prefix(self, normalizer):
"""Invoice number with prefix should extract digits and keep original"""
result = normalizer.normalize('INV-100017500321')
assert 'INV-100017500321' in result
assert '100017500321' in result
assert len(result) == 2
def test_alphanumeric(self, normalizer):
"""Alphanumeric invoice number should extract digits"""
result = normalizer.normalize('ABC123XYZ456')
assert 'ABC123XYZ456' in result
assert '123456' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_whitespace_only(self, normalizer):
"""Whitespace-only string should return empty list"""
result = normalizer(' ')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('INV-12345')
assert 'INV-12345' in result
assert '12345' in result
def test_with_special_characters(self, normalizer):
"""Invoice number with special characters should be normalized"""
result = normalizer.normalize('INV/2025/00123')
assert 'INV/2025/00123' in result
assert '202500123' in result
def test_unicode_normalization(self, normalizer):
"""Unicode zero-width characters should be removed"""
result = normalizer.normalize('INV\u200b123\u200c456')
assert 'INV123456' in result
assert '123456' in result
def test_multiple_dashes(self, normalizer):
"""Invoice number with multiple dashes should be normalized"""
result = normalizer.normalize('INV-2025-001-234')
assert 'INV-2025-001-234' in result
assert '2025001234' in result
def test_no_digits(self, normalizer):
"""Invoice number with no digits should return original only"""
result = normalizer.normalize('ABCDEF')
assert 'ABCDEF' in result
assert len(result) == 1
def test_digits_only_variant_not_duplicated(self, normalizer):
"""Digits-only variant should not be duplicated if same as original"""
result = normalizer.normalize('12345')
assert result == ['12345']

View File

@@ -0,0 +1,65 @@
"""
Tests for OCRNormalizer
Usage:
pytest tests/normalize/normalizers/test_ocr_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.ocr_normalizer import OCRNormalizer
class TestOCRNormalizer:
"""Test OCRNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return OCRNormalizer()
def test_pure_digits(self, normalizer):
"""Pure digit OCR number should return as-is"""
result = normalizer.normalize('94228110015950070')
assert '94228110015950070' in result
assert len(result) == 1
def test_with_prefix(self, normalizer):
"""OCR number with prefix should extract digits and keep original"""
result = normalizer.normalize('OCR: 94228110015950070')
assert 'OCR: 94228110015950070' in result
assert '94228110015950070' in result
def test_with_spaces(self, normalizer):
"""OCR number with spaces should be normalized"""
result = normalizer.normalize('9422 8110 0159 50070')
assert '94228110015950070' in result
def test_with_hyphens(self, normalizer):
"""OCR number with hyphens should be normalized"""
result = normalizer.normalize('1234-5678-9012')
assert '123456789012' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('OCR-12345')
assert '12345' in result
def test_mixed_separators(self, normalizer):
"""OCR number with mixed separators should be normalized"""
result = normalizer.normalize('123 456-789 012')
assert '123456789012' in result
def test_very_long_ocr(self, normalizer):
"""Very long OCR number should be handled"""
result = normalizer.normalize('12345678901234567890')
assert '12345678901234567890' in result

View File

@@ -0,0 +1,83 @@
"""
Tests for OrganisationNumberNormalizer
Usage:
pytest tests/normalize/normalizers/test_organisation_number_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.organisation_number_normalizer import OrganisationNumberNormalizer
class TestOrganisationNumberNormalizer:
"""Test OrganisationNumberNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return OrganisationNumberNormalizer()
def test_with_dash(self, normalizer):
"""Organisation number with dash should generate variants"""
result = normalizer.normalize('556123-4567')
assert '556123-4567' in result
assert '5561234567' in result
def test_without_dash(self, normalizer):
"""Organisation number without dash should generate dash variant"""
result = normalizer.normalize('5561234567')
assert '5561234567' in result
assert '556123-4567' in result
def test_from_vat_number(self, normalizer):
"""VAT number should extract organisation number"""
result = normalizer.normalize('SE556123456701')
assert '5561234567' in result
assert '556123-4567' in result
assert 'SE556123456701' in result
def test_vat_variants(self, normalizer):
"""Organisation number should generate VAT number variants"""
result = normalizer.normalize('556123-4567')
assert 'SE556123456701' in result
# With spaces
vat_with_spaces = [v for v in result if 'SE' in v and ' ' in v]
assert len(vat_with_spaces) > 0
def test_12_digit_with_century(self, normalizer):
"""12-digit organisation number with century should be handled"""
result = normalizer.normalize('165561234567')
assert '5561234567' in result
assert '556123-4567' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('556123-4567')
assert '5561234567' in result
def test_vat_with_spaces(self, normalizer):
"""VAT number with spaces should be normalized"""
result = normalizer.normalize('SE 556123-4567 01')
assert '5561234567' in result
assert 'SE556123456701' in result
def test_mixed_case_vat_prefix(self, normalizer):
"""Mixed case VAT prefix should be normalized"""
result = normalizer.normalize('se556123456701')
assert 'SE556123456701' in result
def test_generates_ocr_variants(self, normalizer):
"""Should generate OCR error variants"""
result = normalizer.normalize('556123-4567')
# Should contain multiple variants including OCR corrections
assert len(result) > 5

View File

@@ -0,0 +1,71 @@
"""
Tests for PlusgiroNormalizer
Usage:
pytest tests/normalize/normalizers/test_plusgiro_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.plusgiro_normalizer import PlusgiroNormalizer
class TestPlusgiroNormalizer:
"""Test PlusgiroNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return PlusgiroNormalizer()
def test_with_dash_8_digits(self, normalizer):
"""8-digit Plusgiro with dash should generate variants"""
result = normalizer.normalize('1234567-8')
assert '1234567-8' in result
assert '12345678' in result
def test_without_dash_8_digits(self, normalizer):
"""8-digit Plusgiro without dash should generate dash variant"""
result = normalizer.normalize('12345678')
assert '12345678' in result
assert '1234567-8' in result
def test_7_digits(self, normalizer):
"""7-digit Plusgiro should be handled"""
result = normalizer.normalize('1234567')
assert '1234567' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('1234567-8')
assert '12345678' in result
def test_with_spaces(self, normalizer):
"""Plusgiro with spaces should be normalized"""
result = normalizer.normalize('1234567 8')
assert '12345678' in result
def test_9_digits(self, normalizer):
"""9-digit Plusgiro should be handled"""
result = normalizer.normalize('123456789')
assert '123456789' in result
def test_with_prefix(self, normalizer):
"""Plusgiro with PG: prefix should be normalized"""
result = normalizer.normalize('PG:1234567-8')
assert '12345678' in result
def test_generates_ocr_variants(self, normalizer):
"""Should generate OCR error variants"""
result = normalizer.normalize('1234567-8')
# Should contain multiple variants including OCR corrections
assert len(result) > 2

View File

@@ -0,0 +1,95 @@
"""
Tests for SupplierAccountsNormalizer
Usage:
pytest tests/normalize/normalizers/test_supplier_accounts_normalizer.py -v
"""
import pytest
from src.normalize.normalizers.supplier_accounts_normalizer import SupplierAccountsNormalizer
class TestSupplierAccountsNormalizer:
"""Test SupplierAccountsNormalizer functionality"""
@pytest.fixture
def normalizer(self):
"""Create normalizer instance for testing"""
return SupplierAccountsNormalizer()
def test_single_plusgiro(self, normalizer):
"""Single Plusgiro account should generate variants"""
result = normalizer.normalize('PG:48676043')
assert 'PG:48676043' in result
assert '48676043' in result
assert '4867604-3' in result
def test_single_bankgiro(self, normalizer):
"""Single Bankgiro account should generate variants"""
result = normalizer.normalize('BG:5393-9484')
assert 'BG:5393-9484' in result
assert '5393-9484' in result
assert '53939484' in result
def test_multiple_accounts(self, normalizer):
"""Multiple accounts separated by | should be handled"""
result = normalizer.normalize('PG:48676043 | PG:49128028 | PG:8915035')
assert '48676043' in result
assert '49128028' in result
assert '8915035' in result
def test_prefix_normalization(self, normalizer):
"""Prefix should be normalized to uppercase"""
result = normalizer.normalize('pg:48676043')
assert 'PG:48676043' in result
def test_prefix_with_space(self, normalizer):
"""Prefix with space should be generated"""
result = normalizer.normalize('PG:48676043')
assert 'PG: 48676043' in result
def test_empty_account_in_list(self, normalizer):
"""Empty accounts in list should be ignored"""
result = normalizer.normalize('PG:48676043 | | PG:49128028')
# Should not crash and should handle both valid accounts
assert '48676043' in result
assert '49128028' in result
def test_account_without_prefix(self, normalizer):
"""Account without prefix should be handled"""
result = normalizer.normalize('48676043')
assert '48676043' in result
assert '4867604-3' in result
def test_7_digit_account(self, normalizer):
"""7-digit account should generate dash format"""
result = normalizer.normalize('4867604')
assert '4867604' in result
assert '486760-4' in result
def test_10_digit_account(self, normalizer):
"""10-digit account (org number format) should be handled"""
result = normalizer.normalize('5561234567')
assert '5561234567' in result
assert '556123-4567' in result
def test_mixed_format_accounts(self, normalizer):
"""Mixed format accounts should all be normalized"""
result = normalizer.normalize('BG:5393-9484 | PG:48676043')
assert '53939484' in result
assert '48676043' in result
def test_empty_string(self, normalizer):
"""Empty string should return empty list"""
result = normalizer('')
assert result == []
def test_none_value(self, normalizer):
"""None value should return empty list"""
result = normalizer(None)
assert result == []
def test_callable_interface(self, normalizer):
"""Normalizer should be callable via __call__"""
result = normalizer('PG:48676043')
assert '48676043' in result