Files
romanett fbdd0af13b Add 1.5.378 <-> 2.0 interop tests; return BadAttributeIdInvalid for unset optional security attributes (#4630)
# Description

Adds an interop test suite between this stack (2.0) and the released
**1.5.378** NuGet stack, in both directions, and fixes a 2.0 bug the
suite found.

## Interop suite

- **`tests/Opc.Ua.Interop.LegacyPeer`** is a console peer built from the
`OPCFoundation.NetStandard.Opc.Ua.*` 1.5.378 NuGet packages.
- It runs as a child process, because the two stacks share assembly
names.
- It deliberately does not import the repository build settings (its own
`Directory.Build.props/targets/Packages.props`).
- Server mode hosts a small interop server with tight limits, or the 1.5
Quickstarts reference server.
- Client mode runs named checks and prints one JSON result line per
check.
- **`tests/Opc.Ua.Interop.Tests`** builds the peer into `legacy-peer/`
and runs in every CI test leg as a normal `*.Tests.csproj`. It covers:
- sessions over None, the RSA policies and the ECC policies (Sign and
SignAndEncrypt), with anonymous and user name logon, plus
read/write/browse/call/subscribe;
- the existing 2.0 `ClientTest` workers run against the 1.5 reference
server, through the new `ClientTestFramework.ExternalServerUrl`;
- custom data types (structures, unions, optional fields, enums) loaded,
decoded and written back by each side's complex type system;
- certificate trust with auto-accept off: untrusted peers, CA-issued
certificates, revoked certificates;
- multi-chunk messages, encoding limits, response size limits, operation
limits, continuation points and security token renewal (RSA and ECC).
- Every peer check is reported as its own test result.
- `-p:LegacyStackVersion=<version>` tests another 1.5 release.
- Docs: `docs/DeveloperGuide.md`.

## 2.0 fix

**Unset optional security attributes now return
`Bad_AttributeIdInvalid`** (`NodeState.ReadNonValueAttribute`).
- Since 3e5f129c6, unset
RolePermissions/UserRolePermissions/AccessRestrictions returned Good
with a null value.
- This made the 1.5 ComplexTypes loader crash against any 2.0 server.
- 1.5 and Part 4 (Read) return `Bad_AttributeIdInvalid` here.

The suite also found two ECC secure-channel bugs: the Sign-only HKDF
salt length, and renewal chaining the previous secret for policies
without SecureChannelEnhancements. #4626 fixed both on master in the
meantime, so this branch now uses master's version. The ECC Sign and ECC
token-renewal interop tests here cover them.
## Reviewer notes

- **Behaviour change:** `NodeStateOptionalStorageTests` previously
asserted Good with a null value for unset optional attributes. It now
asserts `BadAttributeIdInvalid`.
- Monitored items on such unset attributes now fail with
`BadAttributeIdInvalid`, as in 1.5.
- **Known gaps, not changed here:**
- An unset Description still reads Good with a null value (same root
cause).
- A Null write to AccessRestrictions is accepted, as `NodeStateTests`
asserts.
- **Test runtime:** the suite adds roughly 7 minutes per CI test leg. On
net48 the ECC cases skip, because 2.0 has no ECC on .NET Framework.

## Related Issues

- None filed.

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- Ran: interop suite on net10 (188 passed) and on net48 (passed before
the final review fixes), plus Types, Core, InformationModel,
Core.Security and Sessions on net10.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:35:44 +02:00

56 lines
2.0 KiB
C#

/* ========================================================================
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using NUnit.Framework;
using Opc.Ua.Security.Certificates;
using Opc.Ua.Tests;
namespace Opc.Ua.Interop.Tests
{
/// <summary>
/// Assembly-level setup/teardown that verifies no Certificate
/// instances are leaked during the test run.
/// </summary>
[SetUpFixture]
public class LeakDetectionSetup
{
[OneTimeSetUp]
public void GlobalSetup()
{
Certificate.ResetLeakCounters();
}
[OneTimeTearDown]
public void GlobalTeardown()
{
LeakDetectionHelpers.AssertNoCertificateLeaks();
}
}
}