diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml
index fcdbf71d..44d3f8b1 100644
--- a/.github/workflows/demo.yml
+++ b/.github/workflows/demo.yml
@@ -101,7 +101,8 @@ jobs:
--rm jbzoo/csv-blueprint \
validate:csv \
--csv=/parent-host/tests/fixtures/batch/*.csv \
- --schema=/parent-host/tests/schemas/demo_valid.yml
+ --schema=/parent-host/tests/schemas/demo_valid.yml \
+ --ansi
- name: 👎 Invalid CSV file
run: |
@@ -110,64 +111,5 @@ jobs:
--rm jbzoo/csv-blueprint \
validate:csv \
--csv=/parent-host/tests/fixtures/batch/*.csv \
- --schema=/parent-host/tests/schemas/demo_invalid.yml
-
-
- phar:
- name: Phar
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: 8.3
- tools: composer
-
- - name: Build the project
- run: make build --no-print-directory
-
- - name: 👍 Valid CSV file
- run: |
- ./build/csv-blueprint.phar \
- validate:csv \
- --csv=./tests/fixtures/batch/*.csv \
- --schema=./tests/schemas/demo_valid.yml
-
- - name: 👎 Invalid CSV file
- run: |
- ! ./build/csv-blueprint.phar \
- validate:csv \
- --csv=./tests/fixtures/batch/*.csv \
- --schema=./tests/schemas/demo_invalid.yml
-
-
- php:
- name: Pure PHP
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: 8.3
- tools: composer
-
- - name: Build the Project
- run: make build-install --no-print-directory
-
- - name: 👍 Valid CSV file
- run: |
- ./csv-blueprint \
- validate:csv \
- --csv=./tests/fixtures/batch/*.csv \
- --schema=./tests/schemas/demo_valid.yml
-
- - name: 👎 Invalid CSV file
- run: |
- ! ./csv-blueprint \
- validate:csv \
- --csv=./tests/fixtures/batch/*.csv \
- --schema=./tests/schemas/demo_invalid.yml
+ --schema=/parent-host/tests/schemas/demo_invalid.yml \
+ --ansi
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 89eb356c..4d76443f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -139,8 +139,40 @@ jobs:
name: Reports - ${{ matrix.php-version }}
path: build/
- phar:
- name: Phar
+
+ test-php-binary:
+ name: Verify PHP binary
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.3
+ tools: composer
+
+ - name: Build the Project
+ run: make build-install --no-print-directory
+
+ - name: 👍 Valid CSV file
+ run: |
+ ./csv-blueprint \
+ validate:csv \
+ --csv=./tests/fixtures/batch/*.csv \
+ --schema=./tests/schemas/demo_valid.yml
+
+ - name: 👎 Invalid CSV file
+ run: |
+ ! ./csv-blueprint \
+ validate:csv \
+ --csv=./tests/fixtures/batch/*.csv \
+ --schema=./tests/schemas/demo_invalid.yml
+
+
+ test-phar:
+ name: Verify PHAR
runs-on: ubuntu-latest
strategy:
matrix:
@@ -148,26 +180,35 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3
- with:
- fetch-depth: 0
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
- coverage: xdebug
tools: composer
- extensions: ast
- name: Build the project
run: make build --no-print-directory
- - name: Building Phar binary file
- run: make build-phar --no-print-directory
-
- - name: Trying to use the phar file
+ - name: Test help and logo
run: ./build/csv-blueprint.phar
+ - name: 👍 Valid CSV file
+ run: |
+ ./build/csv-blueprint.phar \
+ validate:csv \
+ --csv=./tests/fixtures/batch/*.csv \
+ --schema=./tests/schemas/demo_valid.yml \
+ --ansi
+
+ - name: 👎 Invalid CSV file
+ run: |
+ ! ./build/csv-blueprint.phar \
+ validate:csv \
+ --csv=./tests/fixtures/batch/*.csv \
+ --schema=./tests/schemas/demo_invalid.yml \
+ --ansi
+
- name: Upload Artifacts
uses: actions/upload-artifact@v3
continue-on-error: true
@@ -177,7 +218,7 @@ jobs:
docker:
- name: Docker
+ name: Verify Docker
runs-on: ubuntu-latest
steps:
- name: Checkout code
@@ -186,9 +227,25 @@ jobs:
- name: 🐳 Building Docker Image
run: make build-docker
- - name: Trying to use the Docker Image
+ - name: Test help and logo
run: docker run --rm jbzoo/csv-blueprint --ansi
- - name: Reporting example via Docker
- run: make demo-docker --no-print-directory
- continue-on-error: true
+ - name: 👍 Valid CSV file
+ run: |
+ docker run --rm \
+ -v `pwd`:/parent-host \
+ jbzoo/csv-blueprint \
+ validate:csv \
+ --csv=/parent-host/tests/fixtures/demo.csv \
+ --schema=/parent-host/tests/schemas/demo_valid.yml \
+ --ansi
+
+ - name: 👎 Invalid CSV file
+ run: |
+ ! docker run --rm \
+ -v `pwd`:/parent-host \
+ jbzoo/csv-blueprint \
+ validate:csv \
+ --csv=/parent-host/tests/fixtures/demo.csv \
+ --schema=/parent-host/tests/schemas/demo_invalid.yml \
+ --ansi
diff --git a/Makefile b/Makefile
index b41f5a70..352a8c37 100644
--- a/Makefile
+++ b/Makefile
@@ -12,11 +12,13 @@
.PHONY: build
+REPORT ?= table
+COLUMNS_TEST ?= 150
+
ifneq (, $(wildcard ./vendor/jbzoo/codestyle/src/init.Makefile))
include ./vendor/jbzoo/codestyle/src/init.Makefile
endif
-REPORT ?= table
build: ##@Project Install all 3rd party dependencies
$(call title,"Install/Update all 3rd party dependencies")
diff --git a/README.md b/README.md
index c88fea3e..cbd1252c 100644
--- a/README.md
+++ b/README.md
@@ -4,27 +4,6 @@
[![Stable Version](https://poser.pugx.org/jbzoo/csv-blueprint/version)](https://packagist.org/packages/jbzoo/csv-blueprint/) [![Total Downloads](https://poser.pugx.org/jbzoo/csv-blueprint/downloads)](https://packagist.org/packages/jbzoo/csv-blueprint/stats) [![Docker Pulls](https://img.shields.io/docker/pulls/jbzoo/csv-blueprint.svg)](https://hub.docker.com/r/jbzoo/csv-blueprint) [![Dependents](https://poser.pugx.org/jbzoo/csv-blueprint/dependents)](https://packagist.org/packages/jbzoo/csv-blueprint/dependents?order_by=downloads) [![GitHub License](https://img.shields.io/github/license/jbzoo/csv-blueprint)](https://github.com/JBZoo/Csv-Blueprint/blob/master/LICENSE)
-
-* [Introduction](#introduction)
-* [Why validate CSV files in CI?](#why-validate-csv-files-in-ci)
-* [Features](#features)
-* [Live Demo](#live-demo)
-* [Usage](#usage)
- * [As GitHub Action](#as-github-action)
- * [As Docker container](#as-docker-container)
- * [As PHP binary](#as-php-binary)
- * [As PHP project](#as-php-project)
- * [CLI Help Message](#cli-help-message)
- * [Report examples](#report-examples)
- * [Schema Definition](#schema-definition)
- * [Schema file examples](#schema-file-examples)
-* [Coming soon](#coming-soon)
-* [Disadvantages?](#disadvantages)
-* [Contributing](#contributing)
-* [License](#license)
-* [See Also](#see-also)
-
-
## Introduction
The JBZoo/Csv-Blueprint tool is a powerful and flexible utility designed for validating CSV files against
@@ -232,35 +211,30 @@ Schema: ./tests/schemas/demo_invalid.yml
Found CSV files: 3
(1/3) Invalid file: ./tests/fixtures/batch/demo-1.csv
-+------+------------------+--------------+ demo-1.csv ------------------------------------------+
-| Line | id:Column | Rule | Message |
-+------+------------------+--------------+------------------------------------------------------+
-| 3 | 2:Float | max | Value "74605.944" is greater than "74605" |
-| 3 | 4:Favorite color | allow_values | Value "blue" is not allowed. Allowed values: ["red", |
-| | | | "green", "Blue"] |
-+------+------------------+--------------+ demo-1.csv ------------------------------------------+
++------+------------------+--------------+--------- demo-1.csv --------------------------------------------------+
+| Line | id:Column | Rule | Message |
++------+------------------+--------------+-----------------------------------------------------------------------+
+| 3 | 2:Float | max | Value "74605.944" is greater than "74605" |
+| 3 | 4:Favorite color | allow_values | Value "blue" is not allowed. Allowed values: ["red", "green", "Blue"] |
++------+------------------+--------------+--------- demo-1.csv --------------------------------------------------+
(2/3) Invalid file: ./tests/fixtures/batch/demo-2.csv
-+------+------------+------------+----- demo-2.csv ---------------------------------------+
-| Line | id:Column | Rule | Message |
-+------+------------+------------+--------------------------------------------------------+
-| 2 | 0:Name | min_length | Value "Carl" (length: 4) is too short. Min length is 5 |
-| 2 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date |
-| | | | "1955-05-15T00:00:00.000+00:00" |
-| 4 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date |
-| | | | "1955-05-15T00:00:00.000+00:00" |
-| 5 | 3:Birthday | max_date | Value "2010-07-20" is more than the maximum date |
-| | | | "2009-01-01T00:00:00.000+00:00" |
-| 7 | 0:Name | min_length | Value "Lois" (length: 4) is too short. Min length is 5 |
-+------+------------+------------+----- demo-2.csv ---------------------------------------+
++------+------------+------------+------------------ demo-2.csv ----------------------------------------------------+
+| Line | id:Column | Rule | Message |
++------+------------+------------+----------------------------------------------------------------------------------+
+| 2 | 0:Name | min_length | Value "Carl" (length: 4) is too short. Min length is 5 |
+| 2 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date "1955-05-15T00:00:00.000+00:00" |
+| 4 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date "1955-05-15T00:00:00.000+00:00" |
+| 5 | 3:Birthday | max_date | Value "2010-07-20" is more than the maximum date "2009-01-01T00:00:00.000+00:00" |
+| 7 | 0:Name | min_length | Value "Lois" (length: 4) is too short. Min length is 5 |
++------+------------+------------+------------------ demo-2.csv ----------------------------------------------------+
(3/3) Invalid file: ./tests/fixtures/batch/sub/demo-3.csv
-+------+-----------+------------------+---- demo-3.csv -------------------------------------------+
-| Line | id:Column | Rule | Message |
-+------+-----------+------------------+-----------------------------------------------------------+
-| 0 | | filename_pattern | Filename "./tests/fixtures/batch/sub/demo-3.csv" does not |
-| | | | match pattern: "/demo-[12].csv$/i" |
-+------+-----------+------------------+---- demo-3.csv -------------------------------------------+
++------+-----------+------------------+---------------------- demo-3.csv ------------------------------------------------------------+
+| Line | id:Column | Rule | Message |
++------+-----------+------------------+----------------------------------------------------------------------------------------------+
+| 0 | | filename_pattern | Filename "./tests/fixtures/batch/sub/demo-3.csv" does not match pattern: "/demo-[12].csv$/i" |
++------+-----------+------------------+---------------------- demo-3.csv ------------------------------------------------------------+
Found 8 issues in 3 out of 3 CSV files.
@@ -311,6 +285,8 @@ This gives you great flexibility when validating CSV files.
### Schema file examples
+Available formats: [YAML](schema-examples/full.yml), [JSON](schema-examples/full.json), [PHP](schema-examples/full.php).
+
```yml
# It's a full example of the CSV schema file in YAML format.
@@ -349,11 +325,20 @@ columns:
only_lowercase: true # String is only lower-case. Example: "hello world"
only_uppercase: true # String is only upper-case. Example: "HELLO WORLD"
only_capitalize: true # String is only capitalized. Example: "Hello World"
+ word_count: 10 # Integer only. Exact count of words in the string. Example: "Hello World, 123" - 2 words only (123 is not a word)
+ min_word_count: 1 # Integer only. Min count of words in the string. Example: "Hello World. 123" - 2 words only (123 is not a word)
+ max_word_count: 5 # Integer only. Max count of words in the string Example: "Hello World! 123" - 2 words only (123 is not a word)
+ at_least_contains: [ a, b ] # At least one of the string must be in the CSV value. Case-sensitive.
+ all_must_contain: [ a, b, c ] # All the strings must be part of a CSV value. Case-sensitive.
+ str_ends_with: " suffix" # Case-sensitive. Example: "Hello World suffix"
+ str_starts_with: "prefix " # Case-sensitive. Example: "prefix Hello World"
# Decimal and integer numbers
min: 10 # Can be integer or float, negative and positive
max: 100.50 # Can be integer or float, negative and positive
- precision: 2 # Strict(!) number of digits after the decimal point
+ precision: 3 # Strict(!) number of digits after the decimal point
+ min_precision: 2 # Min number of digits after the decimal point (with zeros)
+ max_precision: 4 # Max number of digits after the decimal point (with zeros)
# Dates
date_format: Y-m-d # See: https://www.php.net/manual/en/datetime.format.php
@@ -371,6 +356,7 @@ columns:
is_uuid4: true # Only UUID4 format. Example: "550e8400-e29b-41d4-a716-446655440000"
is_latitude: true # Can be integer or float. Example: 50.123456
is_longitude: true # Can be integer or float. Example: -89.123456
+ is_alias: true # Only alias format. Example: "my-alias-123"
cardinal_direction: true # Valid cardinal direction. Examples: "N", "S", "NE", "SE", "none", ""
usa_market_name: true # Check if the value is a valid USA market name. Example: "New York, NY"
@@ -379,130 +365,6 @@ columns:
```
-
- Click to see: JSON Format
-
-```json
-{
- "filename_pattern" : "/demo(-\\d+)?\\.csv$/i",
- "csv" : {
- "header" : true,
- "delimiter" : ",",
- "quote_char" : "\\",
- "enclosure" : "\"",
- "encoding" : "utf-8",
- "bom" : false
- },
- "columns" : [
- {
- "name" : "csv_header_name",
- "description" : "Lorem ipsum",
- "rules" : {
- "not_empty" : true,
- "exact_value" : "Some string",
- "allow_values" : ["y", "n", ""],
- "regex" : "\/^[\\d]{2}$\/",
- "min_length" : 1,
- "max_length" : 10,
- "only_trimed" : true,
- "only_lowercase" : true,
- "only_uppercase" : true,
- "only_capitalize" : true,
- "min" : 10,
- "max" : 100.5,
- "precision" : 2,
- "date_format" : "Y-m-d",
- "min_date" : "2000-01-02",
- "max_date" : "+1 day",
- "is_bool" : true,
- "is_int" : true,
- "is_float" : true,
- "is_ip" : true,
- "is_url" : true,
- "is_email" : true,
- "is_domain" : true,
- "is_uuid4" : true,
- "is_latitude" : true,
- "is_longitude" : true,
- "cardinal_direction" : true,
- "usa_market_name" : true
- }
- },
- {"name" : "another_column"}
- ]
-}
-
-```
-
-
-
-
-
-
-
- Click to see: PHP Format
-
-```php
- '/demo(-\\d+)?\\.csv$/i',
-
- 'csv' => [
- 'header' => true,
- 'delimiter' => ',',
- 'quote_char' => '\\',
- 'enclosure' => '"',
- 'encoding' => 'utf-8',
- 'bom' => false,
- ],
-
- 'columns' => [
- [
- 'name' => 'csv_header_name',
- 'description' => 'Lorem ipsum',
- 'rules' => [
- 'not_empty' => true,
- 'exact_value' => 'Some string',
- 'allow_values' => ['y', 'n', ''],
- 'regex' => '/^[\\d]{2}$/',
- 'min_length' => 1,
- 'max_length' => 10,
- 'only_trimed' => true,
- 'only_lowercase' => true,
- 'only_uppercase' => true,
- 'only_capitalize' => true,
- 'min' => 10,
- 'max' => 100.5,
- 'precision' => 2,
- 'date_format' => 'Y-m-d',
- 'min_date' => '2000-01-02',
- 'max_date' => '+1 day',
- 'is_bool' => true,
- 'is_int' => true,
- 'is_float' => true,
- 'is_ip' => true,
- 'is_url' => true,
- 'is_email' => true,
- 'is_domain' => true,
- 'is_uuid4' => true,
- 'is_latitude' => true,
- 'is_longitude' => true,
- 'cardinal_direction' => true,
- 'usa_market_name' => true,
- ],
- ],
- ['name' => 'another_column'],
- ],
-];
-
-```
-
-
-
-
-
## Coming soon
It's random ideas and plans. No orderings and deadlines. But batch processing is the priority #1.
@@ -517,6 +379,8 @@ Batch processing
Validation
* [x] ~~`filename_pattern` validation with regex (like "all files in the folder should be in the format `/^[\d]{4}-[\d]{2}-[\d]{2}\.csv$/`").~~
+* [ ] Flag to ignore file name pattern. It's useful when you have a lot of files and you don't want to validate the file name.
+* [ ] Keyword for null value. Configurable. By default, it's an empty string. But you can use `null`, `nil`, `none`, `empty`, etc.
* [ ] Agregate rules (like "at least one of the fields should be not empty" or "all values must be unique").
* [ ] Handle empty files and files with only a header row, or only with one line of data. One column wthout header is also possible.
* [ ] Using multiple schemas for one csv file.
@@ -531,6 +395,8 @@ Release workflow
* [ ] Build and release Docker image [via GitHub Actions, tags and labels](https://docs.docker.com/build/ci/github-actions/manage-tags-labels/). Review it.
* [ ] Upgrading to PHP 8.3.x
* [ ] Build phar file and release via GitHub Actions.
+* [ ] Auto insert tool version into the Docker image and phar file. It's important to know the version of the tool you are using.
+* [ ] Show version as part of output.
Performance and optimization
* [ ] Parallel validation of really-really large files (1GB+ ?). I know you have them and not so much memory.
@@ -545,6 +411,7 @@ Mock data generation
Reporting
* [ ] More report formats (like JSON, XML, etc). Any ideas?
* [ ] Gitlab and JUnit reports must be as one structure. It's not so easy to implement. But it's a good idea.
+* [ ] Merge reports from multiple CSV files into one report. It's useful when you have a lot of files and you want to see all errors in one place. Especially for GitLab and JUnit reports.
Misc
* [ ] Use it as PHP SDK. Examples in Readme.
diff --git a/composer.lock b/composer.lock
index ff94acda..703111e6 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4355,16 +4355,16 @@
},
{
"name": "phpstan/phpstan",
- "version": "1.10.60",
+ "version": "1.10.62",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe"
+ "reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
- "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd5c8a1660ed3540b211407c77abf4af193a6af9",
+ "reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9",
"shasum": ""
},
"require": {
@@ -4413,7 +4413,7 @@
"type": "tidelift"
}
],
- "time": "2024-03-07T13:30:19+00:00"
+ "time": "2024-03-13T12:27:20+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
@@ -5198,12 +5198,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
- "reference": "bb15a6dc9a8493ace041a6de2929eb63ba0809ef"
+ "reference": "eedc674d89085b0199bd96bfad410404fb2f5dbf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/bb15a6dc9a8493ace041a6de2929eb63ba0809ef",
- "reference": "bb15a6dc9a8493ace041a6de2929eb63ba0809ef",
+ "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/eedc674d89085b0199bd96bfad410404fb2f5dbf",
+ "reference": "eedc674d89085b0199bd96bfad410404fb2f5dbf",
"shasum": ""
},
"conflict": {
@@ -5930,7 +5930,7 @@
"type": "tidelift"
}
],
- "time": "2024-03-10T05:04:21+00:00"
+ "time": "2024-03-13T21:04:41+00:00"
},
{
"name": "sabre/event",
@@ -6851,6 +6851,7 @@
"type": "github"
}
],
+ "abandoned": true,
"time": "2020-09-28T06:45:17+00:00"
},
{
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 9ca3124e..bb58f1ed 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -50,4 +50,8 @@
+
+
+
+
diff --git a/schema-examples/full.json b/schema-examples/full.json
index 17131304..fe841bf1 100644
--- a/schema-examples/full.json
+++ b/schema-examples/full.json
@@ -23,9 +23,18 @@
"only_lowercase" : true,
"only_uppercase" : true,
"only_capitalize" : true,
+ "word_count" : 10,
+ "min_word_count" : 1,
+ "max_word_count" : 5,
+ "at_least_contains" : ["a", "b"],
+ "all_must_contain" : ["a", "b", "c"],
+ "str_ends_with" : " suffix",
+ "str_starts_with" : "prefix ",
"min" : 10,
"max" : 100.5,
- "precision" : 2,
+ "precision" : 3,
+ "min_precision" : 2,
+ "max_precision" : 4,
"date_format" : "Y-m-d",
"min_date" : "2000-01-02",
"max_date" : "+1 day",
@@ -39,6 +48,7 @@
"is_uuid4" : true,
"is_latitude" : true,
"is_longitude" : true,
+ "is_alias" : true,
"cardinal_direction" : true,
"usa_market_name" : true
}
diff --git a/schema-examples/full.php b/schema-examples/full.php
index 66c7542a..f336b00f 100644
--- a/schema-examples/full.php
+++ b/schema-examples/full.php
@@ -41,9 +41,18 @@
'only_lowercase' => true,
'only_uppercase' => true,
'only_capitalize' => true,
+ 'word_count' => 10,
+ 'min_word_count' => 1,
+ 'max_word_count' => 5,
+ 'at_least_contains' => ['a', 'b'],
+ 'all_must_contain' => ['a', 'b', 'c'],
+ 'str_ends_with' => ' suffix',
+ 'str_starts_with' => 'prefix ',
'min' => 10,
'max' => 100.5,
- 'precision' => 2,
+ 'precision' => 3,
+ 'min_precision' => 2,
+ 'max_precision' => 4,
'date_format' => 'Y-m-d',
'min_date' => '2000-01-02',
'max_date' => '+1 day',
@@ -57,6 +66,7 @@
'is_uuid4' => true,
'is_latitude' => true,
'is_longitude' => true,
+ 'is_alias' => true,
'cardinal_direction' => true,
'usa_market_name' => true,
],
diff --git a/schema-examples/full.yml b/schema-examples/full.yml
index 98036c1c..136e73cb 100644
--- a/schema-examples/full.yml
+++ b/schema-examples/full.yml
@@ -47,11 +47,20 @@ columns:
only_lowercase: true # String is only lower-case. Example: "hello world"
only_uppercase: true # String is only upper-case. Example: "HELLO WORLD"
only_capitalize: true # String is only capitalized. Example: "Hello World"
+ word_count: 10 # Integer only. Exact count of words in the string. Example: "Hello World, 123" - 2 words only (123 is not a word)
+ min_word_count: 1 # Integer only. Min count of words in the string. Example: "Hello World. 123" - 2 words only (123 is not a word)
+ max_word_count: 5 # Integer only. Max count of words in the string Example: "Hello World! 123" - 2 words only (123 is not a word)
+ at_least_contains: [ a, b ] # At least one of the string must be in the CSV value. Case-sensitive.
+ all_must_contain: [ a, b, c ] # All the strings must be part of a CSV value. Case-sensitive.
+ str_ends_with: " suffix" # Case-sensitive. Example: "Hello World suffix"
+ str_starts_with: "prefix " # Case-sensitive. Example: "prefix Hello World"
# Decimal and integer numbers
min: 10 # Can be integer or float, negative and positive
max: 100.50 # Can be integer or float, negative and positive
- precision: 2 # Strict(!) number of digits after the decimal point
+ precision: 3 # Strict(!) number of digits after the decimal point
+ min_precision: 2 # Min number of digits after the decimal point (with zeros)
+ max_precision: 4 # Max number of digits after the decimal point (with zeros)
# Dates
date_format: Y-m-d # See: https://www.php.net/manual/en/datetime.format.php
@@ -69,6 +78,7 @@ columns:
is_uuid4: true # Only UUID4 format. Example: "550e8400-e29b-41d4-a716-446655440000"
is_latitude: true # Can be integer or float. Example: 50.123456
is_longitude: true # Can be integer or float. Example: -89.123456
+ is_alias: true # Only alias format. Example: "my-alias-123"
cardinal_direction: true # Valid cardinal direction. Examples: "N", "S", "NE", "SE", "none", ""
usa_market_name: true # Check if the value is a valid USA market name. Example: "New York, NY"
diff --git a/src/Csv/Column.php b/src/Csv/Column.php
index 1cc88b66..3dc820a8 100644
--- a/src/Csv/Column.php
+++ b/src/Csv/Column.php
@@ -17,8 +17,8 @@
namespace JBZoo\CsvBlueprint\Csv;
use JBZoo\CsvBlueprint\Utils;
+use JBZoo\CsvBlueprint\Validators\ColumnValidator;
use JBZoo\CsvBlueprint\Validators\ErrorSuite;
-use JBZoo\CsvBlueprint\Validators\Validator;
use JBZoo\Data\Data;
final class Column
@@ -111,7 +111,7 @@ public function getInherit(): string
public function validate(string $cellValue, int $line): ErrorSuite
{
- return (new Validator($this))->validate($cellValue, $line);
+ return (new ColumnValidator($this))->validate($cellValue, $line);
}
private function prepareRuleSet(string $schemaKey): array
diff --git a/src/Csv/CsvFile.php b/src/Csv/CsvFile.php
index 65d52eb1..543ce522 100644
--- a/src/Csv/CsvFile.php
+++ b/src/Csv/CsvFile.php
@@ -17,8 +17,7 @@
namespace JBZoo\CsvBlueprint\Csv;
use JBZoo\CsvBlueprint\Schema;
-use JBZoo\CsvBlueprint\Utils;
-use JBZoo\CsvBlueprint\Validators\Error;
+use JBZoo\CsvBlueprint\Validators\CsvValidator;
use JBZoo\CsvBlueprint\Validators\ErrorSuite;
use League\Csv\Reader as LeagueReader;
use League\Csv\Statement;
@@ -81,15 +80,7 @@ public function getRecordsChunk(int $offset = 0, int $limit = -1): TabularDataRe
public function validate(bool $quickStop = false): ErrorSuite
{
- $errors = new ErrorSuite($this->getCsvFilename());
-
- $errors
- ->addErrorSuit($this->validateFile($quickStop))
- ->addErrorSuit($this->validateHeader($quickStop))
- ->addErrorSuit($this->validateEachCell($quickStop))
- ->addErrorSuit(self::validateAggregateRules($quickStop));
-
- return $errors;
+ return (new CsvValidator($this, $this->schema))->validate($quickStop);
}
private function prepareReader(): LeagueReader
@@ -108,93 +99,4 @@ private function prepareReader(): LeagueReader
return $reader;
}
-
- private function validateHeader(bool $quickStop = false): ErrorSuite
- {
- $errors = new ErrorSuite();
-
- if (!$this->getCsvStructure()->isHeader()) {
- return $errors;
- }
-
- foreach ($this->schema->getColumns() as $column) {
- if ($column->getName() === '') {
- $error = new Error(
- 'csv.header',
- "Property \"name\" is not defined in schema: \"{$this->schema->getFilename()}\"",
- $column->getHumanName(),
- 1,
- );
-
- $errors->addError($error);
- }
-
- if ($quickStop && $errors->count() > 0) {
- return $errors;
- }
- }
-
- return $errors;
- }
-
- private function validateEachCell(bool $quickStop = false): ErrorSuite
- {
- $errors = new ErrorSuite();
-
- foreach ($this->getRecords() as $line => $record) {
- $columns = $this->schema->getColumnsMappedByHeader($this->getHeader());
-
- foreach ($columns as $column) {
- if ($column === null) {
- continue;
- }
-
- $errors->addErrorSuit($column->validate($record[$column->getKey()], (int)$line + 1));
- if ($quickStop && $errors->count() > 0) {
- return $errors;
- }
- }
- }
-
- return $errors;
- }
-
- private function validateFile(bool $quickStop = false): ErrorSuite
- {
- $errors = new ErrorSuite();
-
- $filenamePattern = $this->schema->getFilenamePattern();
- if (
- $filenamePattern !== null
- && $filenamePattern !== ''
- && \preg_match($filenamePattern, $this->csvFilename) === 0
- ) {
- $error = new Error(
- 'filename_pattern',
- 'Filename "' . Utils::cutPath($this->csvFilename) .
- "\" does not match pattern: \"{$filenamePattern}\"",
- '',
- 0,
- );
-
- $errors->addError($error);
-
- if ($quickStop && $errors->count() > 0) {
- return $errors;
- }
- }
-
- return $errors;
- }
-
- private static function validateAggregateRules(bool $quickStop = false): ErrorSuite
- {
- $errors = new ErrorSuite();
-
- if ($quickStop && $errors->count() > 0) {
- return $errors;
- }
-
- return new ErrorSuite();
- }
}
diff --git a/src/Validators/Rules/AbstarctRule.php b/src/Rules/AbstarctRule.php
similarity index 92%
rename from src/Validators/Rules/AbstarctRule.php
rename to src/Rules/AbstarctRule.php
index d97bcb05..8da530e0 100644
--- a/src/Validators/Rules/AbstarctRule.php
+++ b/src/Rules/AbstarctRule.php
@@ -14,7 +14,7 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
use JBZoo\CsvBlueprint\Utils;
use JBZoo\CsvBlueprint\Validators\Error;
@@ -33,7 +33,7 @@ abstract class AbstarctRule
private string $columnNameId;
private string $ruleCode;
- abstract public function validateRule(?string $cellValue): ?string;
+ abstract public function validateRule(string $cellValue): ?string;
public function __construct(string $columnNameId, null|array|bool|float|int|string $options = null)
{
@@ -42,7 +42,7 @@ public function __construct(string $columnNameId, null|array|bool|float|int|stri
$this->ruleCode = $this->getRuleCode();
}
- public function validate(?string $cellValue, int $line = 0): ?Error
+ public function validate(string $cellValue, int $line = 0): ?Error
{
$error = $this->validateRule($cellValue);
if ($error !== null) {
diff --git a/src/Rules/AllMustContain.php b/src/Rules/AllMustContain.php
new file mode 100644
index 00000000..9a50a1aa
--- /dev/null
+++ b/src/Rules/AllMustContain.php
@@ -0,0 +1,37 @@
+getOptionAsArray();
+ if (\count($inclusions) === 0) {
+ return 'Rule must contain at least one inclusion value in schema file.';
+ }
+
+ foreach ($inclusions as $inclusion) {
+ if (\strpos($cellValue, (string)$inclusion) === false) {
+ return "Value \"{$cellValue}\" must contain all of the following:" .
+ ' "["' . \implode('", "', $inclusions) . '"]"';
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/Validators/Rules/AllowValues.php b/src/Rules/AllowValues.php
similarity index 88%
rename from src/Validators/Rules/AllowValues.php
rename to src/Rules/AllowValues.php
index 11dbcf21..c2f602c5 100644
--- a/src/Validators/Rules/AllowValues.php
+++ b/src/Rules/AllowValues.php
@@ -14,11 +14,11 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
class AllowValues extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$allowedValues = $this->getOptionAsArray();
diff --git a/src/Rules/AtLeastContains.php b/src/Rules/AtLeastContains.php
new file mode 100644
index 00000000..e93161e6
--- /dev/null
+++ b/src/Rules/AtLeastContains.php
@@ -0,0 +1,37 @@
+getOptionAsArray();
+ if (\count($inclusions) === 0) {
+ return 'Rule must contain at least one inclusion value in schema file.';
+ }
+
+ foreach ($inclusions as $inclusion) {
+ if (\strpos($cellValue, (string)$inclusion) !== false) {
+ return null;
+ }
+ }
+
+ return "Value \"{$cellValue}\" must contain one of the following:" .
+ ' "["' . \implode('", "', $inclusions) . '"]"';
+ }
+}
diff --git a/src/Validators/Rules/CardinalDirection.php b/src/Rules/CardinalDirection.php
similarity index 74%
rename from src/Validators/Rules/CardinalDirection.php
rename to src/Rules/CardinalDirection.php
index f1cf9be9..60ca6d92 100644
--- a/src/Validators/Rules/CardinalDirection.php
+++ b/src/Rules/CardinalDirection.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
-class CardinalDirection extends AllowValues
+final class CardinalDirection extends AllowValues
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- return parent::validateRule((string)$cellValue);
+ return parent::validateRule($cellValue);
}
public function getOptionAsArray(): array
diff --git a/src/Validators/Rules/DateFormat.php b/src/Rules/DateFormat.php
similarity index 86%
rename from src/Validators/Rules/DateFormat.php
rename to src/Rules/DateFormat.php
index ec3f80da..232e1046 100644
--- a/src/Validators/Rules/DateFormat.php
+++ b/src/Rules/DateFormat.php
@@ -14,18 +14,18 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class DateFormat extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$expectedDateFormat = $this->getOptionAsString();
if ($expectedDateFormat === '') {
return 'Date format is not defined';
}
- if ($cellValue === null || $cellValue === '') {
+ if ($cellValue === '') {
return 'Date format of value "" is not valid. Expected format: "' . $expectedDateFormat . '"';
}
diff --git a/src/Validators/Rules/ExactValue.php b/src/Rules/ExactValue.php
similarity index 78%
rename from src/Validators/Rules/ExactValue.php
rename to src/Rules/ExactValue.php
index 317aa0cb..2c3aa3d9 100644
--- a/src/Validators/Rules/ExactValue.php
+++ b/src/Rules/ExactValue.php
@@ -14,13 +14,13 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class ExactValue extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
- if ($this->getOptionAsString() !== (string)$cellValue) {
+ if ($this->getOptionAsString() !== $cellValue) {
return "Value \"{$cellValue}\" is not strict equal to " .
"\"{$this->getOptionAsString()}\"";
}
diff --git a/src/Rules/IsAlias.php b/src/Rules/IsAlias.php
new file mode 100644
index 00000000..885c0c7b
--- /dev/null
+++ b/src/Rules/IsAlias.php
@@ -0,0 +1,36 @@
+getOptionAsBool()) {
+ return null;
+ }
+
+ $alias = Filter::alias($cellValue);
+ if ($cellValue !== $alias) {
+ return "Value \"{$cellValue}\" is not a valid alias. Expected \"{$alias}\".";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Validators/Rules/IsBool.php b/src/Rules/IsBool.php
similarity index 77%
rename from src/Validators/Rules/IsBool.php
rename to src/Rules/IsBool.php
index e4260f5b..f1c08a79 100644
--- a/src/Validators/Rules/IsBool.php
+++ b/src/Rules/IsBool.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsBool extends AllowValues
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- return parent::validateRule(\strtolower((string)$cellValue));
+ return parent::validateRule(\strtolower($cellValue));
}
public function getOptionAsArray(): array
diff --git a/src/Validators/Rules/IsDomain.php b/src/Rules/IsDomain.php
similarity index 80%
rename from src/Validators/Rules/IsDomain.php
rename to src/Rules/IsDomain.php
index e815db76..1234350e 100644
--- a/src/Validators/Rules/IsDomain.php
+++ b/src/Rules/IsDomain.php
@@ -14,11 +14,11 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsDomain extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
@@ -26,7 +26,7 @@ public function validateRule(?string $cellValue): ?string
$domainPattern = '/^(?!-)[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})$/';
- if (\preg_match($domainPattern, (string)$cellValue) === 0) {
+ if (\preg_match($domainPattern, $cellValue) === 0) {
return "Value \"{$cellValue}\" is not a valid domain";
}
diff --git a/src/Validators/Rules/IsEmail.php b/src/Rules/IsEmail.php
similarity index 86%
rename from src/Validators/Rules/IsEmail.php
rename to src/Rules/IsEmail.php
index f26cc9dc..d59fbb95 100644
--- a/src/Validators/Rules/IsEmail.php
+++ b/src/Rules/IsEmail.php
@@ -14,11 +14,11 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsEmail extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
diff --git a/src/Validators/Rules/IsFloat.php b/src/Rules/IsFloat.php
similarity index 77%
rename from src/Validators/Rules/IsFloat.php
rename to src/Rules/IsFloat.php
index 644b11aa..ab2f8402 100644
--- a/src/Validators/Rules/IsFloat.php
+++ b/src/Rules/IsFloat.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
class IsFloat extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- if (\preg_match('/^-?\d+(\.\d+)?$/', (string)$cellValue) === 0) {
+ if (\preg_match('/^-?\d+(\.\d+)?$/', $cellValue) === 0) {
return "Value \"{$cellValue}\" is not a float number";
}
diff --git a/src/Validators/Rules/IsInt.php b/src/Rules/IsInt.php
similarity index 78%
rename from src/Validators/Rules/IsInt.php
rename to src/Rules/IsInt.php
index 4581f04a..478376b8 100644
--- a/src/Validators/Rules/IsInt.php
+++ b/src/Rules/IsInt.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsInt extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- if (\preg_match('/^-?\d+$/', (string)$cellValue) === 0) {
+ if (\preg_match('/^-?\d+$/', $cellValue) === 0) {
return "Value \"{$cellValue}\" is not an integer";
}
diff --git a/src/Validators/Rules/IsIp.php b/src/Rules/IsIp.php
similarity index 86%
rename from src/Validators/Rules/IsIp.php
rename to src/Rules/IsIp.php
index ccc5fc98..6ed5cf1e 100644
--- a/src/Validators/Rules/IsIp.php
+++ b/src/Rules/IsIp.php
@@ -14,11 +14,11 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsIp extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
diff --git a/src/Validators/Rules/IsLatitude.php b/src/Rules/IsLatitude.php
similarity index 89%
rename from src/Validators/Rules/IsLatitude.php
rename to src/Rules/IsLatitude.php
index 7a670fb6..27b19b53 100644
--- a/src/Validators/Rules/IsLatitude.php
+++ b/src/Rules/IsLatitude.php
@@ -14,14 +14,14 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsLatitude extends IsFloat
{
private float $min = -90.0;
private float $max = 90.0;
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
diff --git a/src/Validators/Rules/IsLongitude.php b/src/Rules/IsLongitude.php
similarity index 90%
rename from src/Validators/Rules/IsLongitude.php
rename to src/Rules/IsLongitude.php
index 31f2a53b..0188bf5f 100644
--- a/src/Validators/Rules/IsLongitude.php
+++ b/src/Rules/IsLongitude.php
@@ -14,14 +14,14 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsLongitude extends IsFloat
{
private float $min = -180.0;
private float $max = 180.0;
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
diff --git a/src/Validators/Rules/IsUrl.php b/src/Rules/IsUrl.php
similarity index 86%
rename from src/Validators/Rules/IsUrl.php
rename to src/Rules/IsUrl.php
index f2fba082..d57580c9 100644
--- a/src/Validators/Rules/IsUrl.php
+++ b/src/Rules/IsUrl.php
@@ -14,11 +14,11 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsUrl extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
diff --git a/src/Validators/Rules/IsUuid4.php b/src/Rules/IsUuid4.php
similarity index 80%
rename from src/Validators/Rules/IsUuid4.php
rename to src/Rules/IsUuid4.php
index 86a5b55b..37c35fab 100644
--- a/src/Validators/Rules/IsUuid4.php
+++ b/src/Rules/IsUuid4.php
@@ -14,11 +14,11 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class IsUuid4 extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
@@ -26,7 +26,7 @@ public function validateRule(?string $cellValue): ?string
$uuid4 = '/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89ABab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/';
- if (\preg_match($uuid4, (string)$cellValue) === 0) {
+ if (\preg_match($uuid4, $cellValue) === 0) {
return 'Value is not a valid UUID v4';
}
diff --git a/src/Validators/Rules/Max.php b/src/Rules/Max.php
similarity index 87%
rename from src/Validators/Rules/Max.php
rename to src/Rules/Max.php
index 7a0cb9bc..b99d1f72 100644
--- a/src/Validators/Rules/Max.php
+++ b/src/Rules/Max.php
@@ -14,11 +14,11 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class Max extends IsFloat
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$result = parent::validateRule($cellValue);
if ($result !== null) {
diff --git a/src/Validators/Rules/MaxDate.php b/src/Rules/MaxDate.php
similarity index 81%
rename from src/Validators/Rules/MaxDate.php
rename to src/Rules/MaxDate.php
index 3dc0612f..834c7471 100644
--- a/src/Validators/Rules/MaxDate.php
+++ b/src/Rules/MaxDate.php
@@ -14,14 +14,14 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class MaxDate extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$minDate = $this->getOptionAsDate();
- $cellDate = new \DateTimeImmutable((string)$cellValue);
+ $cellDate = new \DateTimeImmutable($cellValue);
if ($cellDate->getTimestamp() > $minDate->getTimestamp()) {
return "Value \"{$cellValue}\" is more than the maximum " .
diff --git a/src/Validators/Rules/MaxLength.php b/src/Rules/MaxLength.php
similarity index 81%
rename from src/Validators/Rules/MaxLength.php
rename to src/Rules/MaxLength.php
index 515cdb74..e34ff02b 100644
--- a/src/Validators/Rules/MaxLength.php
+++ b/src/Rules/MaxLength.php
@@ -14,14 +14,14 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class MaxLength extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$minLength = $this->getOptionAsInt();
- $length = \mb_strlen((string)$cellValue);
+ $length = \mb_strlen($cellValue);
if ($length > $minLength) {
return "Value \"{$cellValue}\" (length: {$length}) is too long. " .
diff --git a/src/Rules/MaxPrecision.php b/src/Rules/MaxPrecision.php
new file mode 100644
index 00000000..1dea055f
--- /dev/null
+++ b/src/Rules/MaxPrecision.php
@@ -0,0 +1,32 @@
+ $this->getOptionAsInt()) {
+ return "Value \"{$cellValue}\" has a precision of {$valuePrecision} " .
+ "but should have a max precision of {$this->getOptionAsInt()}";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/MaxWordCount.php b/src/Rules/MaxWordCount.php
new file mode 100644
index 00000000..99925603
--- /dev/null
+++ b/src/Rules/MaxWordCount.php
@@ -0,0 +1,33 @@
+getOptionAsInt();
+ $count = \str_word_count($cellValue);
+
+ if ($count > $wordCount) {
+ return "Value \"{$cellValue}\" has {$count} words, " .
+ "but must have no more than {$wordCount} words";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Validators/Rules/Min.php b/src/Rules/Min.php
similarity index 87%
rename from src/Validators/Rules/Min.php
rename to src/Rules/Min.php
index ee340fcb..33e7271c 100644
--- a/src/Validators/Rules/Min.php
+++ b/src/Rules/Min.php
@@ -14,11 +14,11 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class Min extends IsFloat
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$result = parent::validateRule($cellValue);
if ($result !== null) {
diff --git a/src/Validators/Rules/MinDate.php b/src/Rules/MinDate.php
similarity index 81%
rename from src/Validators/Rules/MinDate.php
rename to src/Rules/MinDate.php
index e9435a6b..174f73d9 100644
--- a/src/Validators/Rules/MinDate.php
+++ b/src/Rules/MinDate.php
@@ -14,14 +14,14 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class MinDate extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$minDate = $this->getOptionAsDate();
- $cellDate = new \DateTimeImmutable((string)$cellValue);
+ $cellDate = new \DateTimeImmutable($cellValue);
if ($cellDate->getTimestamp() < $minDate->getTimestamp()) {
return "Value \"{$cellValue}\" is less than the minimum " .
diff --git a/src/Validators/Rules/MinLength.php b/src/Rules/MinLength.php
similarity index 81%
rename from src/Validators/Rules/MinLength.php
rename to src/Rules/MinLength.php
index 10120131..0d6b6fe0 100644
--- a/src/Validators/Rules/MinLength.php
+++ b/src/Rules/MinLength.php
@@ -14,14 +14,14 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class MinLength extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$minLength = $this->getOptionAsInt();
- $length = \mb_strlen((string)$cellValue);
+ $length = \mb_strlen($cellValue);
if ($length < $minLength) {
return "Value \"{$cellValue}\" (length: {$length}) is too short. " .
diff --git a/src/Rules/MinPrecision.php b/src/Rules/MinPrecision.php
new file mode 100644
index 00000000..90ceda3e
--- /dev/null
+++ b/src/Rules/MinPrecision.php
@@ -0,0 +1,32 @@
+getOptionAsInt()) {
+ return "Value \"{$cellValue}\" has a precision of {$valuePrecision} " .
+ "but should have a min precision of {$this->getOptionAsInt()}";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/MinWordCount.php b/src/Rules/MinWordCount.php
new file mode 100644
index 00000000..a7fe08a8
--- /dev/null
+++ b/src/Rules/MinWordCount.php
@@ -0,0 +1,33 @@
+getOptionAsInt();
+ $count = \str_word_count($cellValue);
+
+ if ($count < $wordCount) {
+ return "Value \"{$cellValue}\" has {$count} words, " .
+ "but must have at least {$wordCount} words";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Validators/Rules/NotEmpty.php b/src/Rules/NotEmpty.php
similarity index 78%
rename from src/Validators/Rules/NotEmpty.php
rename to src/Rules/NotEmpty.php
index b99339d7..cfa623ca 100644
--- a/src/Validators/Rules/NotEmpty.php
+++ b/src/Rules/NotEmpty.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class NotEmpty extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- if ($cellValue === null || $cellValue === '') {
+ if ($cellValue === '') {
return 'Value is empty';
}
diff --git a/src/Validators/Rules/OnlyCapitalize.php b/src/Rules/OnlyCapitalize.php
similarity index 77%
rename from src/Validators/Rules/OnlyCapitalize.php
rename to src/Rules/OnlyCapitalize.php
index 99b011f2..e25f747d 100644
--- a/src/Validators/Rules/OnlyCapitalize.php
+++ b/src/Rules/OnlyCapitalize.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class OnlyCapitalize extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- if ($cellValue !== null && $cellValue !== \ucfirst($cellValue)) {
+ if ($cellValue !== \ucfirst($cellValue)) {
return "Value \"{$cellValue}\" should be in capitalize";
}
diff --git a/src/Validators/Rules/OnlyLowercase.php b/src/Rules/OnlyLowercase.php
similarity index 77%
rename from src/Validators/Rules/OnlyLowercase.php
rename to src/Rules/OnlyLowercase.php
index 54bd838e..cf36c7f7 100644
--- a/src/Validators/Rules/OnlyLowercase.php
+++ b/src/Rules/OnlyLowercase.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class OnlyLowercase extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- if ($cellValue !== null && $cellValue !== \mb_strtolower($cellValue)) {
+ if ($cellValue !== \mb_strtolower($cellValue)) {
return "Value \"{$cellValue}\" should be in lowercase";
}
diff --git a/src/Validators/Rules/OnlyTrimed.php b/src/Rules/OnlyTrimed.php
similarity index 78%
rename from src/Validators/Rules/OnlyTrimed.php
rename to src/Rules/OnlyTrimed.php
index 60d3b0c8..d8621755 100644
--- a/src/Validators/Rules/OnlyTrimed.php
+++ b/src/Rules/OnlyTrimed.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class OnlyTrimed extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- if (\trim((string)$cellValue) !== (string)$cellValue) {
+ if (\trim($cellValue) !== $cellValue) {
return "Value \"{$cellValue}\" is not trimmed";
}
diff --git a/src/Validators/Rules/OnlyUppercase.php b/src/Rules/OnlyUppercase.php
similarity index 76%
rename from src/Validators/Rules/OnlyUppercase.php
rename to src/Rules/OnlyUppercase.php
index 6546547e..a1034e5e 100644
--- a/src/Validators/Rules/OnlyUppercase.php
+++ b/src/Rules/OnlyUppercase.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
final class OnlyUppercase extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- if ($cellValue !== null && \mb_strtoupper($cellValue, 'UTF-8') !== $cellValue) {
+ if (\mb_strtoupper($cellValue, 'UTF-8') !== $cellValue) {
return "Value \"{$cellValue}\" is not uppercase";
}
diff --git a/src/Validators/Rules/Precision.php b/src/Rules/Precision.php
similarity index 63%
rename from src/Validators/Rules/Precision.php
rename to src/Rules/Precision.php
index f6106e31..697a5b80 100644
--- a/src/Validators/Rules/Precision.php
+++ b/src/Rules/Precision.php
@@ -14,15 +14,15 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
-final class Precision extends AbstarctRule
+class Precision extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$valuePrecision = self::getFloatPrecision($cellValue);
- if ($this->getOptionAsInt() !== $valuePrecision) {
+ if ($valuePrecision !== $this->getOptionAsInt()) {
return "Value \"{$cellValue}\" has a precision of {$valuePrecision} " .
"but should have a precision of {$this->getOptionAsInt()}";
}
@@ -30,15 +30,13 @@ public function validateRule(?string $cellValue): ?string
return null;
}
- private static function getFloatPrecision(?string $cellValue): int
+ protected static function getFloatPrecision(string $cellValue): int
{
- $floatAsString = (string)$cellValue;
- $dotPosition = \strpos($floatAsString, '.');
-
+ $dotPosition = \strpos($cellValue, '.');
if ($dotPosition === false) {
return 0;
}
- return \strlen($floatAsString) - $dotPosition - 1;
+ return \strlen($cellValue) - $dotPosition - 1;
}
}
diff --git a/src/Validators/Rules/Regex.php b/src/Rules/Regex.php
similarity index 82%
rename from src/Validators/Rules/Regex.php
rename to src/Rules/Regex.php
index 356cbdb1..f07ca4d8 100644
--- a/src/Validators/Rules/Regex.php
+++ b/src/Rules/Regex.php
@@ -14,13 +14,13 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
use JBZoo\CsvBlueprint\Utils;
final class Regex extends AbstarctRule
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
$regex = Utils::prepareRegex($this->getOptionAsString());
@@ -28,7 +28,7 @@ public function validateRule(?string $cellValue): ?string
return 'Regex pattern is not defined';
}
- if (\preg_match($regex, (string)$cellValue) === 0) {
+ if (\preg_match($regex, $cellValue) === 0) {
return "Value \"{$cellValue}\" does not match the pattern \"{$regex}\"";
}
diff --git a/src/Validators/Rules/RuleException.php b/src/Rules/RuleException.php
similarity index 90%
rename from src/Validators/Rules/RuleException.php
rename to src/Rules/RuleException.php
index 48ed9cb6..d0e06b41 100644
--- a/src/Validators/Rules/RuleException.php
+++ b/src/Rules/RuleException.php
@@ -14,7 +14,7 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
class RuleException extends \JBZoo\CsvBlueprint\Exception
{
diff --git a/src/Rules/StrEndsWith.php b/src/Rules/StrEndsWith.php
new file mode 100644
index 00000000..aa5dd3ad
--- /dev/null
+++ b/src/Rules/StrEndsWith.php
@@ -0,0 +1,34 @@
+getOptionAsString();
+ if ($prefix === '') {
+ return 'Rule must contain a suffix value in schema file.';
+ }
+
+ if (!\str_ends_with($cellValue, $prefix)) {
+ return "Value \"{$cellValue}\" must end with \"{$prefix}\"";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/StrStartsWith.php b/src/Rules/StrStartsWith.php
new file mode 100644
index 00000000..a16e8f32
--- /dev/null
+++ b/src/Rules/StrStartsWith.php
@@ -0,0 +1,34 @@
+getOptionAsString();
+ if ($prefix === '') {
+ return 'Rule must contain a prefix value in schema file.';
+ }
+
+ if (!\str_starts_with($cellValue, $prefix)) {
+ return "Value \"{$cellValue}\" must start with \"{$prefix}\"";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Validators/Rules/UsaMarketName.php b/src/Rules/UsaMarketName.php
similarity index 74%
rename from src/Validators/Rules/UsaMarketName.php
rename to src/Rules/UsaMarketName.php
index 0979b6ea..0626d44e 100644
--- a/src/Validators/Rules/UsaMarketName.php
+++ b/src/Rules/UsaMarketName.php
@@ -14,17 +14,17 @@
declare(strict_types=1);
-namespace JBZoo\CsvBlueprint\Validators\Rules;
+namespace JBZoo\CsvBlueprint\Rules;
-class UsaMarketName extends AllowValues
+final class UsaMarketName extends AllowValues
{
- public function validateRule(?string $cellValue): ?string
+ public function validateRule(string $cellValue): ?string
{
if (!$this->getOptionAsBool()) {
return null;
}
- if (\preg_match('/^[A-Za-z0-9\s-]+, [A-Z]{2}$/u', (string)$cellValue) === 0) {
+ if (\preg_match('/^[A-Za-z0-9\s-]+, [A-Z]{2}$/u', $cellValue) === 0) {
return "Invalid market name format for value \"{$cellValue}\". " .
'Market name must have format "New York, NY"';
}
diff --git a/src/Rules/WordCount.php b/src/Rules/WordCount.php
new file mode 100644
index 00000000..dc020c6f
--- /dev/null
+++ b/src/Rules/WordCount.php
@@ -0,0 +1,33 @@
+getOptionAsInt();
+ $count = \str_word_count($cellValue);
+
+ if ($count !== $wordCount) {
+ return "Value \"{$cellValue}\" has {$count} words, " .
+ "but must have exactly {$wordCount} words";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Utils.php b/src/Utils.php
index 02b99f78..57beb1cc 100644
--- a/src/Utils.php
+++ b/src/Utils.php
@@ -95,6 +95,12 @@ public static function findFiles(array $paths): array
public static function cutPath(string $fullpath): string
{
- return \str_replace((string)\getcwd(), '.', $fullpath);
+ $pwd = (string)\getcwd();
+
+ if (\strlen($pwd) <= 1) {
+ return $fullpath;
+ }
+
+ return \str_replace($pwd, '.', $fullpath);
}
}
diff --git a/src/Validators/Validator.php b/src/Validators/ColumnValidator.php
similarity index 87%
rename from src/Validators/Validator.php
rename to src/Validators/ColumnValidator.php
index 2d8c857c..be91b832 100644
--- a/src/Validators/Validator.php
+++ b/src/Validators/ColumnValidator.php
@@ -18,7 +18,7 @@
use JBZoo\CsvBlueprint\Csv\Column;
-final class Validator
+final class ColumnValidator
{
private Ruleset $ruleset;
@@ -27,7 +27,7 @@ public function __construct(Column $column)
$this->ruleset = new Ruleset($column->getRules(), $column->getHumanName());
}
- public function validate(?string $cellValue, int $line): ErrorSuite
+ public function validate(string $cellValue, int $line): ErrorSuite
{
return $this->ruleset->validate($cellValue, $line);
}
diff --git a/src/Validators/CsvValidator.php b/src/Validators/CsvValidator.php
new file mode 100644
index 00000000..8169b3bc
--- /dev/null
+++ b/src/Validators/CsvValidator.php
@@ -0,0 +1,134 @@
+csv = $csv;
+ $this->schema = $schema;
+ $this->errors = new ErrorSuite($this->csv->getCsvFilename());
+ }
+
+ public function validate(bool $quickStop = false): ErrorSuite
+ {
+ return $this->errors
+ ->addErrorSuit($this->validateFile($quickStop))
+ ->addErrorSuit($this->validateHeader($quickStop))
+ ->addErrorSuit($this->validateEachCell($quickStop))
+ ->addErrorSuit(self::validateAggregateRules($quickStop));
+ }
+
+ private function validateHeader(bool $quickStop = false): ErrorSuite
+ {
+ $errors = new ErrorSuite();
+
+ if (!$this->schema->getCsvStructure()->isHeader()) {
+ return $errors;
+ }
+
+ foreach ($this->schema->getColumns() as $column) {
+ if ($column->getName() === '') {
+ $error = new Error(
+ 'csv.header',
+ 'Property "name" is not defined in schema: ' .
+ "\"{$this->schema->getFilename()}\"",
+ $column->getHumanName(),
+ 1,
+ );
+
+ $errors->addError($error);
+ }
+
+ if ($quickStop && $errors->count() > 0) {
+ return $errors;
+ }
+ }
+
+ return $errors;
+ }
+
+ private function validateEachCell(bool $quickStop = false): ErrorSuite
+ {
+ $errors = new ErrorSuite();
+
+ foreach ($this->csv->getRecords() as $line => $record) {
+ $columns = $this->schema->getColumnsMappedByHeader($this->csv->getHeader());
+
+ foreach ($columns as $column) {
+ if ($column === null) {
+ continue;
+ }
+
+ $errors->addErrorSuit($column->validate($record[$column->getKey()], (int)$line + 1));
+ if ($quickStop && $errors->count() > 0) {
+ return $errors;
+ }
+ }
+ }
+
+ return $errors;
+ }
+
+ private function validateFile(bool $quickStop = false): ErrorSuite
+ {
+ $errors = new ErrorSuite();
+
+ $filenamePattern = $this->schema->getFilenamePattern();
+ if (
+ $filenamePattern !== null
+ && $filenamePattern !== ''
+ && \preg_match($filenamePattern, $this->csv->getCsvFilename()) === 0
+ ) {
+ $error = new Error(
+ 'filename_pattern',
+ 'Filename "' . Utils::cutPath($this->csv->getCsvFilename()) .
+ "\" does not match pattern: \"{$filenamePattern}\"",
+ '',
+ 0,
+ );
+
+ $errors->addError($error);
+
+ if ($quickStop && $errors->count() > 0) {
+ return $errors;
+ }
+ }
+
+ return $errors;
+ }
+
+ private static function validateAggregateRules(bool $quickStop = false): ErrorSuite
+ {
+ $errors = new ErrorSuite();
+
+ if ($quickStop && $errors->count() > 0) {
+ return $errors;
+ }
+
+ return new ErrorSuite();
+ }
+}
diff --git a/src/Validators/ErrorSuite.php b/src/Validators/ErrorSuite.php
index 1384688c..4a625448 100644
--- a/src/Validators/ErrorSuite.php
+++ b/src/Validators/ErrorSuite.php
@@ -21,6 +21,9 @@
use JBZoo\CIReportConverter\Converters\JUnitConverter;
use JBZoo\CIReportConverter\Converters\TeamCityTestsConverter;
use JBZoo\CIReportConverter\Formats\Source\SourceSuite;
+use JBZoo\Utils\Cli;
+use JBZoo\Utils\Env;
+use JBZoo\Utils\Vars;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Output\BufferedOutput;
@@ -139,15 +142,17 @@ private function renderPlainText(): string
private function renderTable(): string
{
+ $floatingSizes = self::getTableSize();
+
$buffer = new BufferedOutput();
$table = (new Table($buffer))
->setHeaderTitle($this->getTestcaseName())
->setFooterTitle($this->getTestcaseName())
->setHeaders(['Line', 'id:Column', 'Rule', 'Message'])
- ->setColumnMaxWidth(0, 10)
- ->setColumnMaxWidth(1, 20)
- ->setColumnMaxWidth(2, 20)
- ->setColumnMaxWidth(3, 60);
+ ->setColumnMaxWidth(0, $floatingSizes['line'])
+ ->setColumnMaxWidth(1, $floatingSizes['column'])
+ ->setColumnMaxWidth(2, $floatingSizes['rule'])
+ ->setColumnMaxWidth(3, $floatingSizes['message']);
foreach ($this->errors as $error) {
$table->addRow([
@@ -182,4 +187,38 @@ private function getTestcaseName(): string
{
return \pathinfo((string)\realpath((string)$this->csvFilename), \PATHINFO_BASENAME);
}
+
+ /**
+ * Retrieves the size configuration for a table.
+ *
+ * @return int[]
+ */
+ private static function getTableSize(): array
+ {
+ $floatingSizes = [
+ 'line' => 10,
+ 'column' => 20,
+ 'rule' => 20,
+ 'min' => 90,
+ 'max' => 150,
+ 'reserve' => 5, // So that the table does not rest on the very edge of the terminal. Just in case.
+ ];
+
+ // Fallback to 80 if the terminal width cannot be determined.
+ // env.COLUMNS_TEST usually not defined so we use it only for testing purposes.
+ $maxAutoDetected = Env::int('COLUMNS_TEST', Cli::getNumberOfColumns());
+
+ $maxWindowWidth = Vars::limit(
+ $maxAutoDetected,
+ $floatingSizes['min'],
+ $floatingSizes['max'],
+ ) - $floatingSizes['reserve'];
+
+ $floatingSizes['message'] = $maxWindowWidth
+ - $floatingSizes['line']
+ - $floatingSizes['column']
+ - $floatingSizes['rule'];
+
+ return $floatingSizes;
+ }
}
diff --git a/src/Validators/Ruleset.php b/src/Validators/Ruleset.php
index 42efff96..9bccd8c0 100644
--- a/src/Validators/Ruleset.php
+++ b/src/Validators/Ruleset.php
@@ -16,8 +16,8 @@
namespace JBZoo\CsvBlueprint\Validators;
+use JBZoo\CsvBlueprint\Rules\AbstarctRule;
use JBZoo\CsvBlueprint\Utils;
-use JBZoo\CsvBlueprint\Validators\Rules\AbstarctRule;
final class Ruleset
{
@@ -41,7 +41,7 @@ public function __construct(array $rules, string $columnNameId)
*/
public function createRule(string $ruleName, null|array|bool|float|int|string $options = null): AbstarctRule
{
- $classname = __NAMESPACE__ . '\\Rules\\' . Utils::kebabToCamelCase($ruleName);
+ $classname = '\\JBZoo\\CsvBlueprint\\Rules\\' . Utils::kebabToCamelCase($ruleName);
if (\class_exists($classname)) {
// @phpstan-ignore-next-line
return new $classname($this->columnNameId, $options);
@@ -50,7 +50,7 @@ public function createRule(string $ruleName, null|array|bool|float|int|string $o
throw new Exception("Rule \"{$ruleName}\" not found. Expected class: \"{$classname}\"");
}
- public function validate(?string $cellValue, int $line): ErrorSuite
+ public function validate(string $cellValue, int $line): ErrorSuite
{
$errors = new ErrorSuite();
diff --git a/tests/Blueprint/MiscTest.php b/tests/Blueprint/MiscTest.php
index 04b0d277..2ac6f844 100644
--- a/tests/Blueprint/MiscTest.php
+++ b/tests/Blueprint/MiscTest.php
@@ -61,7 +61,7 @@ public function testFullListOfRules(): void
$finder = (new Finder())
->files()
- ->in(PROJECT_ROOT . '/src/Validators/Rules')
+ ->in(PROJECT_ROOT . '/src/Rules')
->ignoreDotFiles(false)
->ignoreVCS(true)
->name('/\\.php$/');
@@ -82,7 +82,13 @@ public function testFullListOfRules(): void
}
\sort($rulesInCode);
- isSame($rulesInCode, $rulesInConfig);
+ $diffAsErrMessage = \array_reduce(
+ \array_diff($rulesInCode, $rulesInConfig),
+ static fn (string $carry, string $item) => $carry . "{$item}: FIXME\n",
+ '',
+ );
+
+ isSame($rulesInCode, $rulesInConfig, $diffAsErrMessage);
}
public function testCsvStrutureDefaultValues(): void
@@ -105,15 +111,15 @@ public function testCheckYmlSchemaExampleInReadme(): void
);
}
- public function testCheckPhpSchemaExampleInReadme(): void
- {
- $this->testCheckExampleInReadme(PROJECT_ROOT . '/schema-examples/full.php', 'php', 'PHP Format', 14);
- }
-
- public function testCheckJsonSchemaExampleInReadme(): void
- {
- $this->testCheckExampleInReadme(PROJECT_ROOT . '/schema-examples/full.json', 'json', 'JSON Format', 0);
- }
+ // public function testCheckPhpSchemaExampleInReadme(): void
+ // {
+ // $this->testCheckExampleInReadme(PROJECT_ROOT . '/schema-examples/full.php', 'php', 'PHP Format', 14);
+ // }
+ //
+ // public function testCheckJsonSchemaExampleInReadme(): void
+ // {
+ // $this->testCheckExampleInReadme(PROJECT_ROOT . '/schema-examples/full.json', 'json', 'JSON Format', 0);
+ // }
public function testCompareExamplesWithOrig(): void
{
@@ -125,8 +131,8 @@ public function testCompareExamplesWithOrig(): void
// file_put_contents("{$basepath}.php", (string)phpArray($origYml));
// file_put_contents("{$basepath}.json", (string)json($origYml));
- isSame($origYml, phpArray("{$basepath}.php")->getArrayCopy(), 'PHP config is invalid');
- isSame($origYml, json("{$basepath}.json")->getArrayCopy(), 'JSON config is invalid');
+ isSame((string)phpArray($origYml), (string)phpArray("{$basepath}.php"), 'PHP config is invalid');
+ isSame((string)json($origYml), (string)json("{$basepath}.json"), 'JSON config is invalid');
}
public function testFindFiles(): void
diff --git a/tests/Blueprint/RulesTest.php b/tests/Blueprint/RulesTest.php
index 69f97883..d7f741d3 100644
--- a/tests/Blueprint/RulesTest.php
+++ b/tests/Blueprint/RulesTest.php
@@ -16,33 +16,43 @@
namespace JBZoo\PHPUnit\Blueprint;
-use JBZoo\CsvBlueprint\Validators\Rules\AllowValues;
-use JBZoo\CsvBlueprint\Validators\Rules\CardinalDirection;
-use JBZoo\CsvBlueprint\Validators\Rules\DateFormat;
-use JBZoo\CsvBlueprint\Validators\Rules\ExactValue;
-use JBZoo\CsvBlueprint\Validators\Rules\IsBool;
-use JBZoo\CsvBlueprint\Validators\Rules\IsDomain;
-use JBZoo\CsvBlueprint\Validators\Rules\IsEmail;
-use JBZoo\CsvBlueprint\Validators\Rules\IsFloat;
-use JBZoo\CsvBlueprint\Validators\Rules\IsInt;
-use JBZoo\CsvBlueprint\Validators\Rules\IsIp;
-use JBZoo\CsvBlueprint\Validators\Rules\IsLatitude;
-use JBZoo\CsvBlueprint\Validators\Rules\IsLongitude;
-use JBZoo\CsvBlueprint\Validators\Rules\IsUrl;
-use JBZoo\CsvBlueprint\Validators\Rules\IsUuid4;
-use JBZoo\CsvBlueprint\Validators\Rules\Max;
-use JBZoo\CsvBlueprint\Validators\Rules\MaxDate;
-use JBZoo\CsvBlueprint\Validators\Rules\MaxLength;
-use JBZoo\CsvBlueprint\Validators\Rules\Min;
-use JBZoo\CsvBlueprint\Validators\Rules\MinDate;
-use JBZoo\CsvBlueprint\Validators\Rules\MinLength;
-use JBZoo\CsvBlueprint\Validators\Rules\NotEmpty;
-use JBZoo\CsvBlueprint\Validators\Rules\OnlyCapitalize;
-use JBZoo\CsvBlueprint\Validators\Rules\OnlyLowercase;
-use JBZoo\CsvBlueprint\Validators\Rules\OnlyUppercase;
-use JBZoo\CsvBlueprint\Validators\Rules\Precision;
-use JBZoo\CsvBlueprint\Validators\Rules\Regex;
-use JBZoo\CsvBlueprint\Validators\Rules\UsaMarketName;
+use JBZoo\CsvBlueprint\Rules\AllMustContain;
+use JBZoo\CsvBlueprint\Rules\AllowValues;
+use JBZoo\CsvBlueprint\Rules\AtLeastContains;
+use JBZoo\CsvBlueprint\Rules\CardinalDirection;
+use JBZoo\CsvBlueprint\Rules\DateFormat;
+use JBZoo\CsvBlueprint\Rules\ExactValue;
+use JBZoo\CsvBlueprint\Rules\IsAlias;
+use JBZoo\CsvBlueprint\Rules\IsBool;
+use JBZoo\CsvBlueprint\Rules\IsDomain;
+use JBZoo\CsvBlueprint\Rules\IsEmail;
+use JBZoo\CsvBlueprint\Rules\IsFloat;
+use JBZoo\CsvBlueprint\Rules\IsInt;
+use JBZoo\CsvBlueprint\Rules\IsIp;
+use JBZoo\CsvBlueprint\Rules\IsLatitude;
+use JBZoo\CsvBlueprint\Rules\IsLongitude;
+use JBZoo\CsvBlueprint\Rules\IsUrl;
+use JBZoo\CsvBlueprint\Rules\IsUuid4;
+use JBZoo\CsvBlueprint\Rules\Max;
+use JBZoo\CsvBlueprint\Rules\MaxDate;
+use JBZoo\CsvBlueprint\Rules\MaxLength;
+use JBZoo\CsvBlueprint\Rules\MaxPrecision;
+use JBZoo\CsvBlueprint\Rules\MaxWordCount;
+use JBZoo\CsvBlueprint\Rules\Min;
+use JBZoo\CsvBlueprint\Rules\MinDate;
+use JBZoo\CsvBlueprint\Rules\MinLength;
+use JBZoo\CsvBlueprint\Rules\MinPrecision;
+use JBZoo\CsvBlueprint\Rules\MinWordCount;
+use JBZoo\CsvBlueprint\Rules\NotEmpty;
+use JBZoo\CsvBlueprint\Rules\OnlyCapitalize;
+use JBZoo\CsvBlueprint\Rules\OnlyLowercase;
+use JBZoo\CsvBlueprint\Rules\OnlyUppercase;
+use JBZoo\CsvBlueprint\Rules\Precision;
+use JBZoo\CsvBlueprint\Rules\Regex;
+use JBZoo\CsvBlueprint\Rules\StrEndsWith;
+use JBZoo\CsvBlueprint\Rules\StrStartsWith;
+use JBZoo\CsvBlueprint\Rules\UsaMarketName;
+use JBZoo\CsvBlueprint\Rules\WordCount;
use JBZoo\PHPUnit\PHPUnit;
use JBZoo\Utils\Str;
@@ -456,13 +466,9 @@ public function testNotEmpty(): void
'"not_empty" at line 0, column "prop". Value is empty.',
\strip_tags((string)$rule->validate('')),
);
- isSame(
- '"not_empty" at line 0, column "prop". Value is empty.',
- \strip_tags((string)$rule->validate(null)),
- );
$rule = new NotEmpty('prop', false);
- isSame(null, $rule->validate(null));
+ isSame(null, $rule->validate(''));
}
public function testOnlyCapitalize(): void
@@ -575,6 +581,75 @@ public function testPrecision(): void
);
}
+ public function testMinPrecision(): void
+ {
+ $rule = new MinPrecision('prop', 0);
+ isSame(null, $rule->validate('0'));
+ isSame(null, $rule->validate('0.0'));
+ isSame(null, $rule->validate('0.1'));
+ isSame(null, $rule->validate('-1.0'));
+ isSame(null, $rule->validate('10.01'));
+ isSame(null, $rule->validate('-10.0001'));
+
+ $rule = new MinPrecision('prop', 1);
+ isSame(null, $rule->validate('0.0'));
+ isSame(null, $rule->validate('10.0'));
+ isSame(null, $rule->validate('-10.0'));
+
+ isSame(
+ '"min_precision" at line 0, column "prop". ' .
+ 'Value "2" has a precision of 0 but should have a min precision of 1.',
+ \strip_tags((string)$rule->validate('2')),
+ );
+
+ $rule = new MinPrecision('prop', 2);
+ isSame(null, $rule->validate('10.01'));
+ isSame(null, $rule->validate('-10.0001'));
+
+ isSame(
+ '"min_precision" at line 0, column "prop". ' .
+ 'Value "2" has a precision of 0 but should have a min precision of 2.',
+ \strip_tags((string)$rule->validate('2')),
+ );
+
+ isSame(
+ '"min_precision" at line 0, column "prop". ' .
+ 'Value "2.0" has a precision of 1 but should have a min precision of 2.',
+ \strip_tags((string)$rule->validate('2.0')),
+ );
+ }
+
+ public function testMaxPrecision(): void
+ {
+ $rule = new MaxPrecision('prop', 0);
+ isSame(null, $rule->validate('0'));
+ isSame(null, $rule->validate('10'));
+ isSame(null, $rule->validate('-10'));
+
+ isSame(
+ '"max_precision" at line 0, column "prop". ' .
+ 'Value "2.0" has a precision of 1 but should have a max precision of 0.',
+ \strip_tags((string)$rule->validate('2.0')),
+ );
+
+ $rule = new MaxPrecision('prop', 1);
+ isSame(null, $rule->validate('0.0'));
+ isSame(null, $rule->validate('10.0'));
+ isSame(null, $rule->validate('-10.0'));
+
+ isSame(
+ '"max_precision" at line 0, column "prop". ' .
+ 'Value "-2.003" has a precision of 3 but should have a max precision of 1.',
+ \strip_tags((string)$rule->validate('-2.003')),
+ );
+
+ isSame(
+ '"max_precision" at line 0, column "prop". ' .
+ 'Value "2.00000" has a precision of 5 but should have a max precision of 1.',
+ \strip_tags((string)$rule->validate('2.00000')),
+ );
+ }
+
public function testRegex(): void
{
$rule = new Regex('prop', '/^a/');
@@ -643,4 +718,179 @@ public function testIsUuid4(): void
$rule = new IsUuid4('prop', false);
isSame(null, $rule->validate('123'));
}
+
+ public function testMustContain(): void
+ {
+ $rule = new AtLeastContains('prop', []);
+ isSame(
+ '"at_least_contains" at line 0, column "prop". ' .
+ 'Rule must contain at least one inclusion value in schema file.',
+ \strip_tags((string)$rule->validate('123')),
+ );
+
+ $rule = new AtLeastContains('prop', ['a', 'b', 'c']);
+ isSame(null, $rule->validate('a'));
+ isSame(null, $rule->validate('abc'));
+ isSame(null, $rule->validate('adasdasdasdc'));
+
+ isSame(
+ '"at_least_contains" at line 0, column "prop". ' .
+ 'Value "123" must contain one of the following: "["a", "b", "c"]".',
+ \strip_tags((string)$rule->validate('123')),
+ );
+ }
+
+ public function testAllMustContain(): void
+ {
+ $rule = new AllMustContain('prop', []);
+ isSame(
+ '"all_must_contain" at line 0, column "prop". ' .
+ 'Rule must contain at least one inclusion value in schema file.',
+ \strip_tags((string)$rule->validate('ac')),
+ );
+
+ $rule = new AllMustContain('prop', ['a', 'b', 'c']);
+ isSame(null, $rule->validate('abc'));
+ isSame(null, $rule->validate('abdasadasdasdc'));
+
+ isSame(
+ '"all_must_contain" at line 0, column "prop". ' .
+ 'Value "ab" must contain all of the following: "["a", "b", "c"]".',
+ \strip_tags((string)$rule->validate('ab')),
+ );
+ isSame(
+ '"all_must_contain" at line 0, column "prop". ' .
+ 'Value "ac" must contain all of the following: "["a", "b", "c"]".',
+ \strip_tags((string)$rule->validate('ac')),
+ );
+ }
+
+ public function testStrStartsWith(): void
+ {
+ $rule = new StrStartsWith('prop', 'a');
+ isSame(null, $rule->validate('a'));
+ isSame(null, $rule->validate('abc'));
+
+ isSame(
+ '"str_starts_with" at line 0, column "prop". Value "" must start with "a".',
+ \strip_tags((string)$rule->validate('')),
+ );
+
+ isSame(
+ '"str_starts_with" at line 0, column "prop". Value " a" must start with "a".',
+ \strip_tags((string)$rule->validate(' a')),
+ );
+
+ $rule = new StrStartsWith('prop', '');
+ isSame(
+ '"str_starts_with" at line 0, column "prop". Rule must contain a prefix value in schema file.',
+ \strip_tags((string)$rule->validate('a ')),
+ );
+ }
+
+ public function testStrEndsWith(): void
+ {
+ $rule = new StrEndsWith('prop', 'a');
+ isSame(null, $rule->validate('a'));
+ isSame(null, $rule->validate('cba'));
+
+ isSame(
+ '"str_ends_with" at line 0, column "prop". Value "" must end with "a".',
+ \strip_tags((string)$rule->validate('')),
+ );
+
+ isSame(
+ '"str_ends_with" at line 0, column "prop". Value "a " must end with "a".',
+ \strip_tags((string)$rule->validate('a ')),
+ );
+
+ $rule = new StrEndsWith('prop', '');
+ isSame(
+ '"str_ends_with" at line 0, column "prop". Rule must contain a suffix value in schema file.',
+ \strip_tags((string)$rule->validate('a ')),
+ );
+ }
+
+ public function testStrWordCount(): void
+ {
+ $rule = new WordCount('prop', 0);
+ isSame(null, $rule->validate(''));
+ isSame(
+ '"word_count" at line 0, column "prop". ' .
+ 'Value "cba" has 1 words, but must have exactly 0 words.',
+ \strip_tags((string)$rule->validate('cba')),
+ );
+
+ $rule = new WordCount('prop', 2);
+ isSame(null, $rule->validate('asd, asdasd'));
+ isSame(
+ '"word_count" at line 0, column "prop". ' .
+ 'Value "cba" has 1 words, but must have exactly 2 words.',
+ \strip_tags((string)$rule->validate('cba')),
+ );
+ isSame(
+ '"word_count" at line 0, column "prop". ' .
+ 'Value "cba 123, 123123" has 1 words, but must have exactly 2 words.',
+ \strip_tags((string)$rule->validate('cba 123, 123123')),
+ );
+
+ isSame(
+ '"word_count" at line 0, column "prop". Value "a b c" has 3 words, but must have exactly 2 words.',
+ \strip_tags((string)$rule->validate('a b c')),
+ );
+ }
+
+ public function testMinWordCount(): void
+ {
+ $rule = new MinWordCount('prop', 0);
+ isSame(null, $rule->validate('cba'));
+
+ $rule = new MinWordCount('prop', 2);
+ isSame(null, $rule->validate('asd, asdasd'));
+ isSame(null, $rule->validate('asd, asdasd asd'));
+ isSame(null, $rule->validate('asd, asdasd 1232 asdas'));
+ isSame(
+ '"min_word_count" at line 0, column "prop". ' .
+ 'Value "cba" has 1 words, but must have at least 2 words.',
+ \strip_tags((string)$rule->validate('cba')),
+ );
+ isSame(
+ '"min_word_count" at line 0, column "prop". ' .
+ 'Value "cba 123, 123123" has 1 words, but must have at least 2 words.',
+ \strip_tags((string)$rule->validate('cba 123, 123123')),
+ );
+ }
+
+ public function testMaxWordCount(): void
+ {
+ $rule = new MaxWordCount('prop', 0);
+ isSame(null, $rule->validate(''));
+
+ $rule = new MaxWordCount('prop', 2);
+ isSame(null, $rule->validate('asd, asdasd'));
+ isSame(null, $rule->validate('asd, 1232'));
+ isSame(null, $rule->validate('asd, 1232 113234324 342 . ..'));
+ isSame(
+ '"max_word_count" at line 0, column "prop". ' .
+ 'Value "asd, asdasd asd 1232 asdas" has 4 words, but must have no more than 2 words.',
+ \strip_tags((string)$rule->validate('asd, asdasd asd 1232 asdas')),
+ );
+ }
+
+ public function testIsAlias(): void
+ {
+ $rule = new IsAlias('prop', true);
+ isSame(null, $rule->validate(''));
+ isSame(null, $rule->validate('123'));
+
+ $rule = new IsAlias('prop', true);
+ isSame(
+ '"is_alias" at line 0, column "prop". ' .
+ 'Value "Qwerty, asd 123" is not a valid alias. Expected "qwerty-asd-123".',
+ \strip_tags((string)$rule->validate('Qwerty, asd 123')),
+ );
+
+ $rule = new IsAlias('prop', false);
+ isSame(null, $rule->validate('Qwerty, asd 123'));
+ }
}
diff --git a/tests/Blueprint/ValidateCsvTest.php b/tests/Blueprint/ValidateCsvTest.php
index a7bc0ec8..780aad04 100644
--- a/tests/Blueprint/ValidateCsvTest.php
+++ b/tests/Blueprint/ValidateCsvTest.php
@@ -64,23 +64,18 @@ public function testValidateOneFileNegativeTable(): void
Found CSV files: 1
(1/1) Invalid file: ./tests/fixtures/demo.csv
- +------+------------------+------------------+--- demo.csv -------------------------------------------------+
- | Line | id:Column | Rule | Message |
- +------+------------------+------------------+--------------------------------------------------------------+
- | 0 | | filename_pattern | Filename "./tests/fixtures/demo.csv" does not match pattern: |
- | | | | "/demo-[12].csv$/i" |
- | 5 | 2:Float | max | Value "74605.944" is greater than "74605" |
- | 5 | 4:Favorite color | allow_values | Value "blue" is not allowed. Allowed values: ["red", |
- | | | | "green", "Blue"] |
- | 6 | 0:Name | min_length | Value "Carl" (length: 4) is too short. Min length is 5 |
- | 6 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date |
- | | | | "1955-05-15T00:00:00.000+00:00" |
- | 8 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date |
- | | | | "1955-05-15T00:00:00.000+00:00" |
- | 9 | 3:Birthday | max_date | Value "2010-07-20" is more than the maximum date |
- | | | | "2009-01-01T00:00:00.000+00:00" |
- | 11 | 0:Name | min_length | Value "Lois" (length: 4) is too short. Min length is 5 |
- +------+------------------+------------------+--- demo.csv -------------------------------------------------+
+ +------+------------------+------------------+------------- demo.csv -----------------------------------------------------------+
+ | Line | id:Column | Rule | Message |
+ +------+------------------+------------------+----------------------------------------------------------------------------------+
+ | 0 | | filename_pattern | Filename "./tests/fixtures/demo.csv" does not match pattern: "/demo-[12].csv$/i" |
+ | 5 | 2:Float | max | Value "74605.944" is greater than "74605" |
+ | 5 | 4:Favorite color | allow_values | Value "blue" is not allowed. Allowed values: ["red", "green", "Blue"] |
+ | 6 | 0:Name | min_length | Value "Carl" (length: 4) is too short. Min length is 5 |
+ | 6 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date "1955-05-15T00:00:00.000+00:00" |
+ | 8 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date "1955-05-15T00:00:00.000+00:00" |
+ | 9 | 3:Birthday | max_date | Value "2010-07-20" is more than the maximum date "2009-01-01T00:00:00.000+00:00" |
+ | 11 | 0:Name | min_length | Value "Lois" (length: 4) is too short. Min length is 5 |
+ +------+------------------+------------------+------------- demo.csv -----------------------------------------------------------+
Found 8 issues in CSV file.
@@ -107,35 +102,30 @@ public function testValidateManyFileNegativeTable(): void
Found CSV files: 3
(1/3) Invalid file: ./tests/fixtures/batch/demo-1.csv
- +------+------------------+--------------+ demo-1.csv ------------------------------------------+
- | Line | id:Column | Rule | Message |
- +------+------------------+--------------+------------------------------------------------------+
- | 3 | 2:Float | max | Value "74605.944" is greater than "74605" |
- | 3 | 4:Favorite color | allow_values | Value "blue" is not allowed. Allowed values: ["red", |
- | | | | "green", "Blue"] |
- +------+------------------+--------------+ demo-1.csv ------------------------------------------+
+ +------+------------------+--------------+--------- demo-1.csv --------------------------------------------------+
+ | Line | id:Column | Rule | Message |
+ +------+------------------+--------------+-----------------------------------------------------------------------+
+ | 3 | 2:Float | max | Value "74605.944" is greater than "74605" |
+ | 3 | 4:Favorite color | allow_values | Value "blue" is not allowed. Allowed values: ["red", "green", "Blue"] |
+ +------+------------------+--------------+--------- demo-1.csv --------------------------------------------------+
(2/3) Invalid file: ./tests/fixtures/batch/demo-2.csv
- +------+------------+------------+----- demo-2.csv ---------------------------------------+
- | Line | id:Column | Rule | Message |
- +------+------------+------------+--------------------------------------------------------+
- | 2 | 0:Name | min_length | Value "Carl" (length: 4) is too short. Min length is 5 |
- | 2 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date |
- | | | | "1955-05-15T00:00:00.000+00:00" |
- | 4 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date |
- | | | | "1955-05-15T00:00:00.000+00:00" |
- | 5 | 3:Birthday | max_date | Value "2010-07-20" is more than the maximum date |
- | | | | "2009-01-01T00:00:00.000+00:00" |
- | 7 | 0:Name | min_length | Value "Lois" (length: 4) is too short. Min length is 5 |
- +------+------------+------------+----- demo-2.csv ---------------------------------------+
+ +------+------------+------------+------------------ demo-2.csv ----------------------------------------------------+
+ | Line | id:Column | Rule | Message |
+ +------+------------+------------+----------------------------------------------------------------------------------+
+ | 2 | 0:Name | min_length | Value "Carl" (length: 4) is too short. Min length is 5 |
+ | 2 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date "1955-05-15T00:00:00.000+00:00" |
+ | 4 | 3:Birthday | min_date | Value "1955-05-14" is less than the minimum date "1955-05-15T00:00:00.000+00:00" |
+ | 5 | 3:Birthday | max_date | Value "2010-07-20" is more than the maximum date "2009-01-01T00:00:00.000+00:00" |
+ | 7 | 0:Name | min_length | Value "Lois" (length: 4) is too short. Min length is 5 |
+ +------+------------+------------+------------------ demo-2.csv ----------------------------------------------------+
(3/3) Invalid file: ./tests/fixtures/batch/sub/demo-3.csv
- +------+-----------+------------------+---- demo-3.csv -------------------------------------------+
- | Line | id:Column | Rule | Message |
- +------+-----------+------------------+-----------------------------------------------------------+
- | 0 | | filename_pattern | Filename "./tests/fixtures/batch/sub/demo-3.csv" does not |
- | | | | match pattern: "/demo-[12].csv$/i" |
- +------+-----------+------------------+---- demo-3.csv -------------------------------------------+
+ +------+-----------+------------------+---------------------- demo-3.csv ------------------------------------------------------------+
+ | Line | id:Column | Rule | Message |
+ +------+-----------+------------------+----------------------------------------------------------------------------------------------+
+ | 0 | | filename_pattern | Filename "./tests/fixtures/batch/sub/demo-3.csv" does not match pattern: "/demo-[12].csv$/i" |
+ +------+-----------+------------------+---------------------- demo-3.csv ------------------------------------------------------------+
Found 8 issues in 3 out of 3 CSV files.
diff --git a/tests/Blueprint/ValidatorTest.php b/tests/Blueprint/ValidatorTest.php
index 4c6a0ffa..e57f125e 100644
--- a/tests/Blueprint/ValidatorTest.php
+++ b/tests/Blueprint/ValidatorTest.php
@@ -43,7 +43,7 @@ protected function setUp(): void
public function testUndefinedRule(): void
{
$this->expectExceptionMessage(
- 'Rule "undefined_rule" not found. Expected class: "JBZoo\CsvBlueprint\Validators\Rules\UndefinedRule"',
+ 'Rule "undefined_rule" not found. Expected class: "\JBZoo\CsvBlueprint\Rules\UndefinedRule"',
);
$csv = new CsvFile(self::CSV_COMPLEX, $this->getRule('seq', 'undefined_rule', true));
$csv->validate();