public void EnsureTestConfigurationPassesSchemaValidation()
        {
            using var schemaReader = new StreamReader(PublishedNodesSchema);
            var validator = new JsonSchemaDotNetSchemaValidator();
            var results   = validator.Validate(Encoding.UTF8.GetBytes(testConfiguration), schemaReader);

            Assert.True(results.All(r => r.IsValid));
        }
        public void IncorrectlyFormattedEndpointUrlsReturnErrors()
        {
            using var schemaReader = new StreamReader(PublishedNodesSchema);

            // Break the EndpointUrl by removing the `.` between `opc` and `tcp` which will
            // trigger a validation failure
            var alteredConfig = testConfiguration.Replace("opc.tcp://20.185.195.172:53530/OPCUA/SimulationServer", "opctcp://20.185.195.172:53530/OPCUA/SimulationServer");
            var validator     = new JsonSchemaDotNetSchemaValidator();
            var results       = validator.Validate(Encoding.UTF8.GetBytes(alteredConfig), schemaReader);

            // Ensure that we failed the regex NodeID check
            Assert.Equal("The string value was not a match for the indicated regular expression", results.ElementAt(1).Message);
        }
        public void EnsureTestConfigurationWithBOMHeaderPassesSchemaValidation()
        {
            using var ms     = new MemoryStream();
            using var writer = new StreamWriter(ms, new UTF8Encoding(true)); // Write with BOM header.
            writer.Write(testConfiguration);
            writer.Flush();
            ms.Seek(0, SeekOrigin.Begin);
            var testConfigurationBytes = ms.ReadAsBuffer().ToArray();

            using var schemaReader = new StreamReader("Default/publishednodesschema.json");
            var validator = new JsonSchemaDotNetSchemaValidator();
            var results   = validator.Validate(testConfigurationBytes, schemaReader);

            Assert.True(results.All(r => r.IsValid));
        }
        public void ByteStringNodeIdsThatAreNotByteStingsReturnErrors()
        {
            using var schemaReader = new StreamReader(PublishedNodesSchema);

            // Break the id of the first Node to ensure an error is thrown
            var alteredConfig = testConfiguration.Replace("i=1001", "b=12345");
            var validator     = new JsonSchemaDotNetSchemaValidator();
            var results       = validator.Validate(Encoding.UTF8.GetBytes(alteredConfig), schemaReader);

            // if the default schema includes ID Value Checks
            if (DefaultSchemaIncludesIdValuePatterns)
            {
                // Ensure that we failed the regex NodeID check
                Assert.Equal("The string value was not a match for the indicated regular expression", results.ElementAt(1).Message);
            }
        }
        public void ValidateTrivialStringTypeSchema()
        {
            var schema = @"
{
    ""type"": ""object"",
    ""properties"": {
                ""prop"": { ""type"": ""string"" }
    }
}
";
            var pn     = @"{""prop"": ""this is a string""}";

            var validator = new JsonSchemaDotNetSchemaValidator();
            var results   = validator.Validate(Encoding.UTF8.GetBytes(pn), new StringReader(schema));

            Assert.True(results.All(r => r.IsValid));
        }
        public void ConfigurationFileWithLargeErrorListIsHandledSuccessfully()
        {
            using var schemaReader = new StreamReader(PublishedNodesSchema);
            var configFileShell = @"
[
  {
    ""EndpointUrl"": ""opc.tcp://20.185.195.172:53530/OPCUA/SimulationServer"",
    ""UseSecurity"": false,
    ""OpcNodes"": [ placeholder ]
  }
]
";
            var longNodeList    = new List <string>();

            // Add 10000 nodes into the temp config and make a broken replacement
            // of `Id` with `ID` to trigger schema failures.
            for (int i = 0; i < 10000; i++)
            {
                longNodeList.Add(@"
      {
        ""Id"": ""i = 1001"",
        ""OpcSamplingInterval"": 2000,
        ""OpcPublishingInterval"": 5000,
      }
".Replace("Id", "ID"));
            }

            var longBadConfig = configFileShell.Replace("placeholder", string.Join(",", longNodeList));
            var validator     = new JsonSchemaDotNetSchemaValidator();
            var results       = validator.Validate(Encoding.UTF8.GetBytes(longBadConfig), schemaReader);

            // if the default schema includes ID Value Checks
            if (DefaultSchemaIncludesIdValuePatterns)
            {
                // Ensure that we get all 20002 errors back.
                Assert.Equal(20002, results.Count);

                // Ensure that we failed on schema checks correctly.
                Assert.Equal(1, results.Count(r => r.Message.Equals("Expected 1 matching subschema but found 0")));
                Assert.Equal(1, results.Count(r => r.Message.Equals("Required properties [NodeId] were not present")));
                Assert.Equal(10000, results.Count(r => r.Message.Equals("Required properties [Id] were not present")));
                Assert.Equal(10000, results.Count(r => r.Message.Equals("Required properties [ExpandedNodeId] were not present")));
            }
        }
        public void EnsureNumericDataFailsOnStringTypeValidation()
        {
            var schema = @"
{
    ""type"": ""object"",
    ""properties"": {
                ""prop"": { ""type"": ""string"" }
    }
}
";

            var pn = @"{""prop"": 1000}";

            var validator = new JsonSchemaDotNetSchemaValidator();
            var results   = validator.Validate(Encoding.UTF8.GetBytes(pn), new StringReader(schema));

            Assert.True(results.All(r => !r.IsValid));
            Assert.Equal("Value is number but should be string", results.First().Message);
        }
        public void EnsureSchemaTrailingCommaSupportInBuffers()
        {
            var schema = @"
{
    ""type"": ""object"",
    ""properties"": {
                ""prop"": { ""type"": ""string"" }
    }
}
";

            // The following object includes a trailing comma
            var pn = @" { ""prop"": ""this is a string"", } ";

            var validator = new JsonSchemaDotNetSchemaValidator();
            var results   = validator.Validate(Encoding.UTF8.GetBytes(pn), new StringReader(schema));

            Assert.True(results.All(r => r.IsValid));
        }
        // There are nuances to the use of "items" schema for arrays in JSON Schema.
        // Particularly the difference between "items: [{ ... }]" and "items: { ... }"
        // The first checks ONLY the schema of the array element in a given array index position,
        // whereas just object notation for "items" (e.g. '{}') will ensure that all elements are
        // checked against a given schema.
        // See: https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.4.1
        public void MultipleIntNodeIdsThatAreNotIntsReturnExpectedErrors()
        {
            using var schemaReader = new StreamReader(PublishedNodesSchema);

            // Break the id of the second Node to ensure an error is thrown
            var alteredConfig = testConfiguration.Replace("i=1002", "'i=12345f'");

            //Break the id of the second to last Node (#8) to ensure an error is thrown
            alteredConfig = alteredConfig.Replace("i=12345", "'i=12345g'");
            var validator = new JsonSchemaDotNetSchemaValidator();
            var results   = validator.Validate(Encoding.UTF8.GetBytes(alteredConfig), schemaReader);

            // if the default schema includes ID Value Checks
            if (DefaultSchemaIncludesIdValuePatterns)
            {
                Assert.NotEmpty(results);
                // Ensure that we failed the regex Id check on the 2nd node
                Assert.Equal("The string value was not a match for the indicated regular expression", results.ElementAt(1).Message);
                // Ensure that we failed the regex ExpandedNodeId check on the 9th node.
                Assert.Equal("Required properties [ExpandedNodeId] were not present", results.ElementAt(8).Message);
            }
        }
        public void EnsureMultipleValidationErrorsAreFlattened()
        {
            var schema = @"
{
    ""type"": ""object"",
    ""properties"": {
                ""prop"": { ""type"": ""string"" },
                ""prop2"": { ""type"": ""integer"" }
    }
}
";

            var pn = @" { ""prop"": 1000, ""prop2"":""this is a string"" } ";

            var validator = new JsonSchemaDotNetSchemaValidator();
            var results   = validator.Validate(Encoding.UTF8.GetBytes(pn), new StringReader(schema));

            Assert.Equal(3, results.Count);
            Assert.True(results.All(r => !r.IsValid));

            // Ensure that the return error messages are what we expect from the
            // schema validation library
            Assert.Equal(1, results.Where(r =>
                                          !r.IsValid &&
                                          r.InstanceLocation == "#" &&
                                          r.SchemaLocation == "#/properties" &&
                                          r.Message == null).Take(3).Count());
            Assert.Equal(1, results.Where(r =>
                                          !r.IsValid &&
                                          r.InstanceLocation == "#/prop" &&
                                          r.SchemaLocation == "#/properties/prop/type" &&
                                          r.Message == "Value is number but should be string").Take(3).Count());
            Assert.Equal(1, results.Where(r =>
                                          !r.IsValid &&
                                          r.InstanceLocation == "#/prop2" &&
                                          r.SchemaLocation == "#/properties/prop2/type" &&
                                          r.Message == "Value is string but should be integer").Take(3).Count());
        }
        public void ValidateHistoricalMixedModeSchema()
        {
            var configFileShell = @"
[
    {
        ""EndpointUrl"": ""opc.tcp://crpogace01:49320"",
        ""UseSecurity"": false,
        ""OpcNodes"": [
            {

                ""DisplayName"": ""Kep_1_DisplayName"",
		        ""DataSetFieldId"": ""Kep_1_DataSetFieldId"",
		        ""Id"": ""nsu=KEPServerEX;s=Sim.CH1.SIM_CH1_TAG1\\234754a-c63-b9601"",
		        ""OpcSamplingInterval"": 1000,
		        ""OpcPublishingInterval"": 1000
            },
	        {
                ""DisplayName"": ""Kep_2_DisplayName"",
		        ""DataSetFieldId"": ""Kep_2_DataSetFieldId"",
		        ""Id"": ""ns=2;s=Sim.CH1.SIM_CH1_TAG10\\2347798-c63-19401"",
		        ""ExpandedNodeId"":""nsu=KEPServerEX;s=Sim.CH1.SIM_CH1_TAG10\\2347798-c63-19401"",
		        ""OpcSamplingInterval"": 1000,
		        ""OpcPublishingInterval"": 1000
            }
        ]
	},
	{
	    ""EndpointUrl"": ""opc.tcp://crpogace01:51210/UA/DemoServer"",
	    ""UseSecurity"": false,
	    ""OpcNodes"": [
			{
				""DisplayName"": ""Softing_1_DisplayName"",
				""DataSetFieldId"": ""Softing_1_DataSetFieldId"",
				""Id"": ""http://test.org/UA/Data/#i=10847"",
				""OpcSamplingInterval"": 1000,
				""OpcPublishingInterval"": 1000

            },
			{
				""DisplayName"": ""Softing_2_DisplayName"",
				""DataSetFieldId"": ""Softing_2_DataSetFieldId"",
				""Id"": ""nsu=http://test.org/UA/Data/;i=10848"",
				""OpcSamplingInterval"": 1000,
				""OpcPublishingInterval"": 1000
			},
			{
                ""DisplayName"": ""Softing_3_DisplayName"",
				""DataSetFieldId"": ""Softing_3_DataSetFieldId"",
				""Id"": ""ns=3;i=10849"",
				""ExpandedNodeId"": ""http://test.org/UA/Data/#i=10849"",
				""OpcSamplingInterval"": 1000,
				""OpcPublishingInterval"": 1000
            }
		]
	}
]";

            using var schemaReader = new StreamReader(PublishedNodesSchema);

            var validator = new JsonSchemaDotNetSchemaValidator();
            var results   = validator.Validate(Encoding.UTF8.GetBytes(configFileShell), schemaReader);

            if (!DefaultSchemaIncludesIdValuePatterns)
            {
                Assert.True(results.First().IsValid);
            }
            else
            {
                Assert.Equal(12, results.Count());
                // Ensure that we failed the regex Id check on the 2nd node
                Assert.Equal("Expected 1 matching subschema but found 0", results.ElementAt(1).Message);
                // Ensure that we failed the regex ExpandedNodeId check on the 9th node.
                Assert.Equal("The string value was not a match for the indicated regular expression", results.ElementAt(2).Message);
            }
        }