/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2026 3A Systems, LLC. */ package org.opends.server.tools.upgrade; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; import org.forgerock.opendj.ldap.DN; import org.forgerock.opendj.ldap.Entry; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.ldif.LDIFEntryReader; import org.forgerock.opendj.ldif.LDIFEntryWriter; import org.opends.server.DirectoryServerTestCase; import org.opends.server.TestCaseUtils; import org.opends.server.util.ChangeOperationType; import org.opends.server.util.StaticUtils; import org.testng.annotations.Test; import static org.testng.Assert.*; /** * Tests that the issue #851 upgrade task payloads, applied through * {@link UpgradeUtils#updateConfigFile}, add the RFC 5805 transaction extended operation handler * entries exactly once and match the fresh-install template. */ @SuppressWarnings("javadoc") @Test(groups = { "precommit", "tools" }, sequential = true) public class UpgradeUtilsTestCase extends DirectoryServerTestCase { /** Unknown config attributes must not fail parsing, as in the upgrade tool's own schema. */ private final Schema schema = Schema.getCoreSchema().asNonStrictSchema(); /** The config.ldif template a fresh install starts from. */ private File freshInstallTemplate() { return new File(TestCaseUtils.getBuildRoot(), "resource/config/config.ldif"); } /** The upgrade task must apply exactly what a fresh install ships. */ @Test public void testTaskPayloadsMirrorFreshInstallTemplate() throws Exception { final List template = readEntries(freshInstallTemplate()); assertEntryPresentOnce(template, Upgrade.START_TRANSACTION_HANDLER_ENTRY); assertEntryPresentOnce(template, Upgrade.END_TRANSACTION_HANDLER_ENTRY); } @Test public void testAddTransactionHandlersAppliesOnceAndIsIdempotent() throws Exception { final File tempDir = TestCaseUtils.createTemporaryDirectory("upgradeTask851"); try { // Simulates an instance upgraded from a pre-4.10.0 version: the fresh-install // template with the two transaction handler entries missing. final File config = new File(tempDir, "config.ldif"); writeConfigWithoutTransactionEntries(config); assertEquals(applyAdd(config, Upgrade.START_TRANSACTION_HANDLER_ENTRY), 1); assertEquals(applyAdd(config, Upgrade.END_TRANSACTION_HANDLER_ENTRY), 1); assertEquals(applyAdd(config, Upgrade.START_TRANSACTION_HANDLER_ENTRY), 0); assertEquals(applyAdd(config, Upgrade.END_TRANSACTION_HANDLER_ENTRY), 0); final List entries = readEntries(config); assertEntryPresentOnce(entries, Upgrade.START_TRANSACTION_HANDLER_ENTRY); assertEntryPresentOnce(entries, Upgrade.END_TRANSACTION_HANDLER_ENTRY); } finally { StaticUtils.recursiveDelete(tempDir); } } private int applyAdd(final File config, final String... ldifLines) throws Exception { return UpgradeUtils.updateConfigFile(config, null, ChangeOperationType.ADD, ldifLines); } private void writeConfigWithoutTransactionEntries(final File config) throws Exception { final DN startDN = dnOf(Upgrade.START_TRANSACTION_HANDLER_ENTRY); final DN endDN = dnOf(Upgrade.END_TRANSACTION_HANDLER_ENTRY); int removed = 0; try (LDIFEntryReader reader = new LDIFEntryReader(new FileInputStream(freshInstallTemplate())).setSchema(schema); LDIFEntryWriter writer = new LDIFEntryWriter(new FileOutputStream(config))) { while (reader.hasNext()) { final Entry entry = reader.readEntry(); if (entry.getName().equals(startDN) || entry.getName().equals(endDN)) { removed++; continue; } writer.writeEntry(entry); } } assertEquals(removed, 2, "fresh-install template no longer ships the transaction entries"); } private void assertEntryPresentOnce(final List entries, final String... ldifLines) throws Exception { final DN dn = dnOf(ldifLines); final List matches = new ArrayList<>(); for (final Entry entry : entries) { if (entry.getName().equals(dn)) { matches.add(entry); } } assertEquals(matches.size(), 1, "expected exactly one entry " + dn); try (LDIFEntryReader reader = new LDIFEntryReader(ldifLines).setSchema(schema)) { assertEquals(matches.get(0), reader.readEntry()); } } private DN dnOf(final String... ldifLines) { return DN.valueOf(ldifLines[0].replaceFirst("dn: ", ""), schema); } private List readEntries(final File ldifFile) throws Exception { final List entries = new ArrayList<>(); try (LDIFEntryReader reader = new LDIFEntryReader(new FileInputStream(ldifFile)).setSchema(schema)) { while (reader.hasNext()) { entries.add(reader.readEntry()); } } return entries; } }