1
0
Fork 0
mirror of https://github.com/composer/composer synced 2025-05-10 09:02:59 +00:00

Merge remote-tracking branch 'guilliamxavier/lax-schema'

This commit is contained in:
Jordi Boggiano 2021-05-24 22:27:48 +02:00
commit 084fff2014
No known key found for this signature in database
GPG key ID: 7BBD42C429EC80BC
6 changed files with 116 additions and 75 deletions

View file

@ -94,9 +94,7 @@ Out of the box, Composer supports four types:
CMSs like the [SilverStripe installer](https://github.com/silverstripe/silverstripe-installer) CMSs like the [SilverStripe installer](https://github.com/silverstripe/silverstripe-installer)
or full fledged applications distributed as packages. This can for example or full fledged applications distributed as packages. This can for example
be used by IDEs to provide listings of projects to initialize when creating be used by IDEs to provide listings of projects to initialize when creating
a new workspace. Setting the type to `project` also makes the `name` and a new workspace.
`description` fields optional, making it a good choice for closed source
projects wishing to use `composer validate`.
- **metapackage:** An empty package that contains requirements and will trigger - **metapackage:** An empty package that contains requirements and will trigger
their installation, but contains no files and will not write anything to the their installation, but contains no files and will not write anything to the
filesystem. As such, it does not require a dist or source key to be filesystem. As such, it does not require a dist or source key to be

View file

@ -2,27 +2,6 @@
"$schema": "https://json-schema.org/draft-04/schema#", "$schema": "https://json-schema.org/draft-04/schema#",
"title": "Package", "title": "Package",
"type": "object", "type": "object",
"additionalProperties": false,
"oneOf": [
{
"properties": {
"type": {
"not": {
"enum": ["project"]
}
}
},
"required": ["name", "description"]
},
{
"properties": {
"type": {
"enum": ["project"]
}
},
"required": ["type"]
}
],
"properties": { "properties": {
"name": { "name": {
"type": "string", "type": "string",

View file

@ -196,9 +196,9 @@ class JsonFile
$schemaData = (object) array('$ref' => $schemaFile); $schemaData = (object) array('$ref' => $schemaFile);
if ($schema === self::LAX_SCHEMA) { if ($schema !== self::LAX_SCHEMA) {
$schemaData->additionalProperties = true; $schemaData->additionalProperties = false;
$schemaData->oneOf = null; $schemaData->required = array('name', 'description');
} }
$validator = new Validator(); $validator = new Validator();

View file

@ -63,16 +63,6 @@ class ConfigValidator
$json->validateSchema(); $json->validateSchema();
} catch (JsonValidationException $e) { } catch (JsonValidationException $e) {
foreach ($e->getErrors() as $message) { foreach ($e->getErrors() as $message) {
if ($message === 'type : The property type is required') {
$message .= ' (see https://getcomposer.org/doc/04-schema.md#type)';
}
if ($message === 'name : The property name is required') {
$message .= ' (or set "type" to "project" to remove this requirement)';
}
if ($message === 'description : The property description is required') {
$message .= ' (or set "type" to "project" to remove this requirement)';
}
if ($laxValid) { if ($laxValid) {
$publishErrors[] = $message; $publishErrors[] = $message;
} else { } else {

View file

@ -37,44 +37,6 @@ class ComposerSchemaTest extends TestCase
$this->assertEquals($expectedError, $this->check($json)); $this->assertEquals($expectedError, $this->check($json));
} }
public function testRequiredProperties()
{
$json = '{ }';
$result = $this->check($json);
$this->assertContains(array('property' => 'type', 'message' => 'The property type is required', 'constraint' => 'required'), $result);
$this->assertContains(array('property' => 'name', 'message' => 'The property name is required', 'constraint' => 'required'), $result);
$this->assertContains(array('property' => 'description', 'message' => 'The property description is required', 'constraint' => 'required'), $result);
$this->assertContains(array('property' => '', 'message' => 'Failed to match exactly one schema', 'constraint' => 'oneOf'), $result);
$json = '{ "name": "vendor/package" }';
$this->assertEquals(array(
array('property' => 'type', 'message' => 'The property type is required', 'constraint' => 'required'),
array('property' => 'description', 'message' => 'The property description is required', 'constraint' => 'required'),
array('property' => '', 'message' => 'Failed to match exactly one schema', 'constraint' => 'oneOf'),
), $this->check($json));
$json = '{ "description": "generic description" }';
$this->assertEquals(array(
array('property' => 'type', 'message' => 'The property type is required', 'constraint' => 'required'),
array('property' => 'name', 'message' => 'The property name is required', 'constraint' => 'required'),
array('property' => '', 'message' => 'Failed to match exactly one schema', 'constraint' => 'oneOf'),
), $this->check($json));
$json = '{ "type": "library" }';
$this->assertEquals(array(
array('property' => 'type', 'message' => 'Does not have a value in the enumeration ["project"]', 'constraint' => 'enum', 'enum' => array('project')),
array('property' => 'name', 'message' => 'The property name is required', 'constraint' => 'required'),
array('property' => 'description', 'message' => 'The property description is required', 'constraint' => 'required'),
array('property' => '', 'message' => 'Failed to match exactly one schema', 'constraint' => 'oneOf'),
), $this->check($json));
$json = '{ "type": "project" }';
$this->assertTrue($this->check($json));
$json = '{ "name": "vendor/package", "description": "description" }';
$this->assertTrue($this->check($json));
}
public function testOptionalAbandonedProperty() public function testOptionalAbandonedProperty()
{ {
$json = '{"name": "vendor/package", "description": "description", "abandoned": true}'; $json = '{"name": "vendor/package", "description": "description", "abandoned": true}';

View file

@ -14,6 +14,7 @@ namespace Composer\Test\Json;
use Seld\JsonLint\ParsingException; use Seld\JsonLint\ParsingException;
use Composer\Json\JsonFile; use Composer\Json\JsonFile;
use Composer\Json\JsonValidationException;
use Composer\Test\TestCase; use Composer\Test\TestCase;
class JsonFileTest extends TestCase class JsonFileTest extends TestCase
@ -93,6 +94,117 @@ class JsonFileTest extends TestCase
{ {
$json = new JsonFile(__DIR__.'/Fixtures/composer.json'); $json = new JsonFile(__DIR__.'/Fixtures/composer.json');
$this->assertTrue($json->validateSchema()); $this->assertTrue($json->validateSchema());
$this->assertTrue($json->validateSchema(JsonFile::LAX_SCHEMA));
}
public function testSchemaValidationError()
{
$file = tempnam(sys_get_temp_dir(), 'c');
file_put_contents($file, '{ "name": null }');
$json = new JsonFile($file);
$expectedMessage = sprintf('"%s" does not match the expected JSON schema', $file);
$expectedError = 'name : NULL value found, but a string is required';
try {
$json->validateSchema();
$this->fail('Expected exception to be thrown (strict)');
} catch (JsonValidationException $e) {
$this->assertEquals($expectedMessage, $e->getMessage());
$this->assertContains($expectedError, $e->getErrors());
}
try {
$json->validateSchema(JsonFile::LAX_SCHEMA);
$this->fail('Expected exception to be thrown (lax)');
} catch (JsonValidationException $e) {
$this->assertEquals($expectedMessage, $e->getMessage());
$this->assertContains($expectedError, $e->getErrors());
}
unlink($file);
}
public function testSchemaValidationLaxAdditionalProperties()
{
$file = tempnam(sys_get_temp_dir(), 'c');
file_put_contents($file, '{ "name": "vendor/package", "description": "generic description", "foo": "bar" }');
$json = new JsonFile($file);
try {
$json->validateSchema();
$this->fail('Expected exception to be thrown (strict)');
} catch (JsonValidationException $e) {
$this->assertEquals(sprintf('"%s" does not match the expected JSON schema', $file), $e->getMessage());
$this->assertEquals(array('The property foo is not defined and the definition does not allow additional properties'), $e->getErrors());
}
$this->assertTrue($json->validateSchema(JsonFile::LAX_SCHEMA));
unlink($file);
}
public function testSchemaValidationLaxRequired()
{
$file = tempnam(sys_get_temp_dir(), 'c');
$json = new JsonFile($file);
$expectedMessage = sprintf('"%s" does not match the expected JSON schema', $file);
file_put_contents($file, '{ }');
try {
$json->validateSchema();
$this->fail('Expected exception to be thrown (strict)');
} catch (JsonValidationException $e) {
$this->assertEquals($expectedMessage, $e->getMessage());
$errors = $e->getErrors();
$this->assertContains('name : The property name is required', $errors);
$this->assertContains('description : The property description is required', $errors);
}
$this->assertTrue($json->validateSchema(JsonFile::LAX_SCHEMA));
file_put_contents($file, '{ "name": "vendor/package" }');
try {
$json->validateSchema();
$this->fail('Expected exception to be thrown (strict)');
} catch (JsonValidationException $e) {
$this->assertEquals($expectedMessage, $e->getMessage());
$this->assertEquals(array('description : The property description is required'), $e->getErrors());
}
$this->assertTrue($json->validateSchema(JsonFile::LAX_SCHEMA));
file_put_contents($file, '{ "description": "generic description" }');
try {
$json->validateSchema();
$this->fail('Expected exception to be thrown (strict)');
} catch (JsonValidationException $e) {
$this->assertEquals($expectedMessage, $e->getMessage());
$this->assertEquals(array('name : The property name is required'), $e->getErrors());
}
$this->assertTrue($json->validateSchema(JsonFile::LAX_SCHEMA));
file_put_contents($file, '{ "type": "library" }');
try {
$json->validateSchema();
$this->fail('Expected exception to be thrown (strict)');
} catch (JsonValidationException $e) {
$this->assertEquals($expectedMessage, $e->getMessage());
$errors = $e->getErrors();
$this->assertContains('name : The property name is required', $errors);
$this->assertContains('description : The property description is required', $errors);
}
$this->assertTrue($json->validateSchema(JsonFile::LAX_SCHEMA));
file_put_contents($file, '{ "type": "project" }');
try {
$json->validateSchema();
$this->fail('Expected exception to be thrown (strict)');
} catch (JsonValidationException $e) {
$this->assertEquals($expectedMessage, $e->getMessage());
$errors = $e->getErrors();
$this->assertContains('name : The property name is required', $errors);
$this->assertContains('description : The property description is required', $errors);
}
$this->assertTrue($json->validateSchema(JsonFile::LAX_SCHEMA));
file_put_contents($file, '{ "name": "vendor/package", "description": "generic description" }');
$this->assertTrue($json->validateSchema());
$this->assertTrue($json->validateSchema(JsonFile::LAX_SCHEMA));
unlink($file);
} }
public function testParseErrorDetectMissingCommaMultiline() public function testParseErrorDetectMissingCommaMultiline()