blob: e0dd484e09bcd86bc8a126bb0589d6307864421b [file] [edit]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "validator/tracker.h"
#include "absl/strings/string_view.h"
#include "constants/status_codes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "proto/manifest.pb.h"
#include "proto/validation_status.pb.h"
#include "testing/proto_test_utils.h"
namespace credentio {
namespace {
using ::credentio_testing::EqualsProto;
using ::credentio_testing::ParseTextProtoOrDie;
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::Property;
constexpr absl::string_view kManifestLabel = "urn:c2pa:9999";
TEST(ValidationTrackerTest, RecordSuccess) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
validation_tracker.RecordSuccess(SuccessStatusCode::kClaimSignatureValidated,
{});
EXPECT_FALSE(validation_tracker.HasFailures());
EXPECT_THAT(validation_tracker.GetSuccesses(),
ElementsAre("claimSignature.validated"));
EXPECT_THAT(validation_tracker.GetSuccessStatuses(),
ElementsAre(Property(&ValidationStatus::code,
"claimSignature.validated")));
EXPECT_THAT(validation_tracker.GetFailures(), IsEmpty());
EXPECT_THAT(validation_tracker.GetInformationals(), IsEmpty());
EXPECT_THAT(manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(
R"pb(successes {
code: "claimSignature.validated"
url: "self#jumbf=/c2pa/urn:c2pa:9999"
})pb")));
}
TEST(ValidationTrackerTest, RecordInformational) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
validation_tracker.RecordInformational(
InformationalStatusCode::kIngredientUnknownProvenance, {});
EXPECT_FALSE(validation_tracker.HasFailures());
EXPECT_THAT(validation_tracker.GetSuccesses(), IsEmpty());
EXPECT_THAT(validation_tracker.GetSuccessStatuses(), IsEmpty());
EXPECT_THAT(validation_tracker.GetInformationals(),
ElementsAre("ingredient.unknownProvenance"));
EXPECT_THAT(validation_tracker.GetInformationalStatuses(),
ElementsAre(Property(&ValidationStatus::code,
"ingredient.unknownProvenance")));
EXPECT_THAT(validation_tracker.GetFailures(), IsEmpty());
EXPECT_THAT(manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(
R"pb(informationals {
code: "ingredient.unknownProvenance"
url: "self#jumbf=/c2pa/urn:c2pa:9999"
})pb")));
}
TEST(ValidationTrackerTest, RecordInformationalWithMessage) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
validation_tracker.RecordInformational(
InformationalStatusCode::kIngredientUnknownProvenance,
{.explanation = "some message"});
EXPECT_FALSE(validation_tracker.HasFailures());
EXPECT_THAT(validation_tracker.GetSuccesses(), IsEmpty());
EXPECT_THAT(validation_tracker.GetSuccessStatuses(), IsEmpty());
EXPECT_THAT(validation_tracker.GetInformationals(),
ElementsAre("ingredient.unknownProvenance"));
EXPECT_THAT(validation_tracker.GetFailures(), IsEmpty());
EXPECT_THAT(manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(
R"pb(informationals {
code: "ingredient.unknownProvenance"
url: "self#jumbf=/c2pa/urn:c2pa:9999"
explanation: "some message"
})pb")));
}
TEST(ValidationTrackerTest, RecordFailure) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
validation_tracker.RecordFailure(FailureStatusCode::kClaimSignatureMissing,
{});
EXPECT_TRUE(validation_tracker.HasFailures());
EXPECT_THAT(validation_tracker.GetSuccesses(), IsEmpty());
EXPECT_THAT(validation_tracker.GetSuccessStatuses(), IsEmpty());
EXPECT_THAT(validation_tracker.GetFailures(),
ElementsAre("claimSignature.missing"));
EXPECT_THAT(validation_tracker.GetInformationals(), IsEmpty());
EXPECT_THAT(manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(
R"pb(failures {
code: "claimSignature.missing"
url: "self#jumbf=/c2pa/urn:c2pa:9999"
})pb")));
}
TEST(ValidationTrackerTest, RecordFailureWithMessage) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
validation_tracker.RecordFailure(FailureStatusCode::kClaimMissing, {});
validation_tracker.RecordFailure(FailureStatusCode::kClaimSignatureMissing,
{.explanation = "some message"});
EXPECT_TRUE(validation_tracker.HasFailures());
EXPECT_THAT(
validation_tracker.GetFailureStatuses(),
ElementsAre(Property(&ValidationStatus::code, "claim.missing"),
Property(&ValidationStatus::code, "claimSignature.missing")));
EXPECT_THAT(
manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(R"pb(
failures { code: "claim.missing" url: "self#jumbf=/c2pa/urn:c2pa:9999" }
failures {
code: "claimSignature.missing"
url: "self#jumbf=/c2pa/urn:c2pa:9999"
explanation: "some message"
}
)pb")));
}
TEST(ValidationTrackerTest, AssertionTracker) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
ValidationTracker assertion_tracker =
validation_tracker.AssertionTracker("testlabel.v2");
assertion_tracker.RecordFailure(
FailureStatusCode::kAssertionActionMalformed,
{.url = "self#jumbf=/url/override", .explanation = "message1"});
assertion_tracker.RecordFailure(FailureStatusCode::kAssertionActionMalformed,
{.explanation = "message2"});
EXPECT_TRUE(assertion_tracker.HasFailures());
EXPECT_TRUE(validation_tracker.HasFailures());
EXPECT_THAT(
assertion_tracker.GetFailureStatuses(),
ElementsAre(
Property(&ValidationStatus::code, "assertion.action.malformed"),
Property(&ValidationStatus::code, "assertion.action.malformed")));
EXPECT_THAT(
manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(R"pb(
failures {
code: "assertion.action.malformed"
url: "self#jumbf=/url/override"
explanation: "message1"
}
failures {
code: "assertion.action.malformed"
url: "self#jumbf=/c2pa/urn:c2pa:9999/c2pa.assertions/testlabel.v2"
explanation: "message2"
}
)pb")));
}
TEST(ValidationTrackerTest, HasFailures) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
validation_tracker.RecordFailure(FailureStatusCode::kClaimSignatureMissing,
{});
validation_tracker.RecordSuccess(SuccessStatusCode::kClaimSignatureValidated,
{});
EXPECT_TRUE(validation_tracker.HasFailures());
}
TEST(ValidationTrackerTest, NothingRecorded) {
Manifest manifest;
manifest.set_label(kManifestLabel);
{
ValidationTracker validation_tracker(&manifest);
}
EXPECT_TRUE(manifest.has_validation());
}
TEST(ValidationTrackerTest, RecordSuccessFullUrl) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
validation_tracker.RecordSuccess(SuccessStatusCode::kClaimSignatureValidated,
{.url = "self#jumbf=/c2pa/urn:c2pa:1234"});
EXPECT_THAT(manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(
R"pb(successes {
code: "claimSignature.validated"
url: "self#jumbf=/c2pa/urn:c2pa:1234"
})pb")));
}
TEST(ValidationTrackerTest, RecordSuccessPartialUrl) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
validation_tracker.RecordSuccess(SuccessStatusCode::kClaimSignatureValidated,
{.url = "suffix"});
EXPECT_THAT(manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(
R"pb(successes {
code: "claimSignature.validated"
url: "self#jumbf=/c2pa/urn:c2pa:9999/suffix"
})pb")));
}
TEST(ValidationTrackerTest, MergeStatuses) {
Manifest manifest;
manifest.set_label(kManifestLabel);
ValidationTracker validation_tracker(&manifest);
ValidationStatusSet statuses;
ValidationStatus* failure = statuses.add_failures();
failure->set_code("claim.missing");
failure->set_url("some/url");
failure->set_explanation("some explanation");
ValidationStatus* success = statuses.add_successes();
success->set_code("claimSignature.validated");
success->set_url("self#jumbf=/c2pa/other/url");
ValidationStatus* informational = statuses.add_informationals();
informational->set_code("ingredient.unknownProvenance");
validation_tracker.MergeStatuses(statuses);
EXPECT_THAT(manifest.validation(),
EqualsProto(ParseTextProtoOrDie<ValidationStatusSet>(R"pb(
failures {
code: "claim.missing"
url: "self#jumbf=/c2pa/urn:c2pa:9999/some/url"
explanation: "some explanation"
}
successes {
code: "claimSignature.validated"
url: "self#jumbf=/c2pa/other/url"
}
informationals {
code: "ingredient.unknownProvenance"
url: "self#jumbf=/c2pa/urn:c2pa:9999"
}
)pb")));
}
} // namespace
} // namespace credentio