From 2a0f48f8e3927e92a203d7c4321f6ae8b8c09c93 Mon Sep 17 00:00:00 2001 From: Aarush Arora Date: Tue, 22 Sep 2026 13:51:08 -0700 Subject: [PATCH] Persist chat deprecation acknowledgement --- .../DeprecationAcknowledgmentStore.java | 107 ++++++++++++++++++ .../configuration/PluginStoreKeys.java | 1 + .../assets/ChatClientConfiguration.java | 39 +++++++ .../assets/ChatWebViewAssetProvider.java | 12 +- .../views/AmazonQChatViewActionHandler.java | 14 ++- .../ChatPromptAcknowledgmentHandler.java | 32 ++++++ .../DeprecationAcknowledgmentStoreTest.java | 100 ++++++++++++++++ .../assets/ChatClientConfigurationTest.java | 82 ++++++++++++++ .../ChatPromptAcknowledgmentHandlerTest.java | 97 ++++++++++++++++ 9 files changed, 473 insertions(+), 11 deletions(-) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/configuration/DeprecationAcknowledgmentStore.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatClientConfiguration.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatPromptAcknowledgmentHandler.java create mode 100644 plugin/tst/software/aws/toolkits/eclipse/amazonq/configuration/DeprecationAcknowledgmentStoreTest.java create mode 100644 plugin/tst/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatClientConfigurationTest.java create mode 100644 plugin/tst/software/aws/toolkits/eclipse/amazonq/views/ChatPromptAcknowledgmentHandlerTest.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/configuration/DeprecationAcknowledgmentStore.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/configuration/DeprecationAcknowledgmentStore.java new file mode 100644 index 000000000..8d403a207 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/configuration/DeprecationAcknowledgmentStore.java @@ -0,0 +1,107 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.configuration; + +import org.eclipse.core.runtime.preferences.ConfigurationScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.osgi.service.prefs.BackingStoreException; + +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; +import software.aws.toolkits.eclipse.amazonq.util.LoggingService; + +/** + * Persists deprecation acknowledgements at the Eclipse installation level when possible. + * + *

Some Eclipse installations do not allow writes to the configuration scope. In those + * environments, acknowledgements fall back to the existing workspace-scoped plugin store. + */ +public final class DeprecationAcknowledgmentStore { + + private static final String STORAGE_PROBE_KEY = "deprecationAcknowledgmentStorageProbe"; + + private final IEclipsePreferences installationPreferences; + private final PluginStore workspaceFallback; + private final LoggingService logger; + + private boolean storageChecked; + private boolean useWorkspaceFallback; + + public DeprecationAcknowledgmentStore(final IEclipsePreferences installationPreferences, + final PluginStore workspaceFallback, final LoggingService logger) { + this.installationPreferences = installationPreferences; + this.workspaceFallback = workspaceFallback; + this.logger = logger; + } + + public static DeprecationAcknowledgmentStore getInstance() { + return InstanceHolder.INSTANCE; + } + + public synchronized boolean isAcknowledged(final String key) { + ensureStorageAvailable(); + if (useWorkspaceFallback) { + return "true".equals(workspaceFallback.get(key)); + } + try { + return "true".equals(installationPreferences.get(key, null)); + } catch (RuntimeException e) { + switchToWorkspaceFallback("reading", key, e); + return "true".equals(workspaceFallback.get(key)); + } + } + + public synchronized void acknowledge(final String key) { + ensureStorageAvailable(); + if (useWorkspaceFallback) { + workspaceFallback.put(key, "true"); + return; + } + try { + installationPreferences.put(key, "true"); + installationPreferences.flush(); + } catch (BackingStoreException | RuntimeException e) { + switchToWorkspaceFallback("saving", key, e); + workspaceFallback.put(key, "true"); + } + } + + private void ensureStorageAvailable() { + if (storageChecked) { + return; + } + storageChecked = true; + try { + installationPreferences.put(STORAGE_PROBE_KEY, "true"); + installationPreferences.flush(); + installationPreferences.remove(STORAGE_PROBE_KEY); + installationPreferences.flush(); + } catch (BackingStoreException | RuntimeException e) { + try { + installationPreferences.remove(STORAGE_PROBE_KEY); + } catch (RuntimeException ignored) { + // The installation scope is already known to be unavailable. + } + switchToWorkspaceFallback("validating", STORAGE_PROBE_KEY, e); + } + } + + private void switchToWorkspaceFallback(final String operation, final String key, final Exception exception) { + useWorkspaceFallback = true; + if (logger != null) { + logger.warn(String.format( + "Unable to use installation-level preferences while %s deprecation acknowledgement '%s'; using workspace preferences", + operation, key), exception); + } + } + + private static final class InstanceHolder { + private static final DeprecationAcknowledgmentStore INSTANCE = new DeprecationAcknowledgmentStore( + ConfigurationScope.INSTANCE.getNode(Activator.PLUGIN_ID), + Activator.getPluginStore(), + Activator.getLogger()); + + private InstanceHolder() { + } + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/configuration/PluginStoreKeys.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/configuration/PluginStoreKeys.java index 5787ad11c..8cf91c69a 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/configuration/PluginStoreKeys.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/configuration/PluginStoreKeys.java @@ -11,5 +11,6 @@ private PluginStoreKeys() { public static final String CHAT_DISCLAIMER_ACKNOWLEDGED = "qchatDisclaimerAcknowledged"; public static final String PAIR_PROGRAMMING_ACKNOWLEDGED = "qchatPairProgrammingAcknowledged"; + public static final String CHAT_DEPRECATION_NOTICE_ACKNOWLEDGED = "qchatDeprecationNoticeAcknowledged"; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatClientConfiguration.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatClientConfiguration.java new file mode 100644 index 000000000..681eb469a --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatClientConfiguration.java @@ -0,0 +1,39 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.providers.assets; + +import software.aws.toolkits.eclipse.amazonq.configuration.DeprecationAcknowledgmentStore; +import software.aws.toolkits.eclipse.amazonq.configuration.PluginStore; +import software.aws.toolkits.eclipse.amazonq.configuration.PluginStoreKeys; + +final class ChatClientConfiguration { + + private final boolean disclaimerAcknowledged; + private final boolean pairProgrammingAcknowledged; + private final boolean deprecationNoticeAcknowledged; + + private ChatClientConfiguration(final boolean disclaimerAcknowledged, + final boolean pairProgrammingAcknowledged, final boolean deprecationNoticeAcknowledged) { + this.disclaimerAcknowledged = disclaimerAcknowledged; + this.pairProgrammingAcknowledged = pairProgrammingAcknowledged; + this.deprecationNoticeAcknowledged = deprecationNoticeAcknowledged; + } + + static ChatClientConfiguration load(final PluginStore pluginStore, + final DeprecationAcknowledgmentStore deprecationAcknowledgmentStore) { + return new ChatClientConfiguration( + "true".equals(pluginStore.get(PluginStoreKeys.CHAT_DISCLAIMER_ACKNOWLEDGED)), + "true".equals(pluginStore.get(PluginStoreKeys.PAIR_PROGRAMMING_ACKNOWLEDGED)), + deprecationAcknowledgmentStore.isAcknowledged( + PluginStoreKeys.CHAT_DEPRECATION_NOTICE_ACKNOWLEDGED)); + } + + String toJavaScript() { + return String.format(""" + disclaimerAcknowledged: %b, + pairProgrammingAcknowledged: %b, + deprecationNoticeAcknowledged: %b, + """, disclaimerAcknowledged, pairProgrammingAcknowledged, deprecationNoticeAcknowledged); + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java index 3b7f2905e..b142a3e23 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java @@ -16,7 +16,7 @@ import software.aws.toolkits.eclipse.amazonq.broker.events.ChatWebViewAssetState; import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager; import software.aws.toolkits.eclipse.amazonq.chat.ChatTheme; -import software.aws.toolkits.eclipse.amazonq.configuration.PluginStoreKeys; +import software.aws.toolkits.eclipse.amazonq.configuration.DeprecationAcknowledgmentStore; import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.providers.lsp.LspManagerProvider; import software.aws.toolkits.eclipse.amazonq.util.PluginPlatform; @@ -187,8 +187,8 @@ private String getEclipseWebkitScript() { } private String generateJS(final String jsEntrypoint) { - var disclaimerAcknowledged = Activator.getPluginStore().get(PluginStoreKeys.CHAT_DISCLAIMER_ACKNOWLEDGED); - var pairProgrammingAcknowledged = Activator.getPluginStore().get(PluginStoreKeys.PAIR_PROGRAMMING_ACKNOWLEDGED); + var clientConfiguration = ChatClientConfiguration.load( + Activator.getPluginStore(), DeprecationAcknowledgmentStore.getInstance()); return String.format(""" - """, jsEntrypoint, getWaitFunction(), "true".equals(disclaimerAcknowledged), "true".equals(pairProgrammingAcknowledged), - getInputFunctions()); + """, jsEntrypoint, getWaitFunction(), clientConfiguration.toJavaScript(), getInputFunctions()); } @SuppressWarnings("MethodLength") diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java index 7e383652b..d494072c5 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java @@ -23,6 +23,7 @@ import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager; import software.aws.toolkits.eclipse.amazonq.chat.ChatMessage; import software.aws.toolkits.eclipse.amazonq.chat.models.CursorState; +import software.aws.toolkits.eclipse.amazonq.configuration.DeprecationAcknowledgmentStore; import software.aws.toolkits.eclipse.amazonq.configuration.PluginStoreKeys; import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.AuthFollowUpType; @@ -36,9 +37,17 @@ public class AmazonQChatViewActionHandler implements ViewActionHandler { private ChatCommunicationManager chatCommunicationManager; + private final ChatPromptAcknowledgmentHandler promptAcknowledgmentHandler; public AmazonQChatViewActionHandler(final ChatCommunicationManager chatCommunicationManager) { + this(chatCommunicationManager, new ChatPromptAcknowledgmentHandler( + Activator.getPluginStore(), DeprecationAcknowledgmentStore.getInstance())); + } + + AmazonQChatViewActionHandler(final ChatCommunicationManager chatCommunicationManager, + final ChatPromptAcknowledgmentHandler promptAcknowledgmentHandler) { this.chatCommunicationManager = chatCommunicationManager; + this.promptAcknowledgmentHandler = promptAcknowledgmentHandler; } /* @@ -120,10 +129,7 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser @SuppressWarnings("unchecked") Map options = (Map) message.getData(); String messageId = options.get("messageId"); - - if ("programmerModeCardId".equals(messageId)) { - Activator.getPluginStore().put(PluginStoreKeys.PAIR_PROGRAMMING_ACKNOWLEDGED, "true"); - } + promptAcknowledgmentHandler.acknowledge(messageId); break; case GET_SERIALIZED_CHAT: ChatAsyncResultManager.getInstance().setResult(parsedCommand.getRequestId(), message.getData()); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatPromptAcknowledgmentHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatPromptAcknowledgmentHandler.java new file mode 100644 index 000000000..da520bed3 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatPromptAcknowledgmentHandler.java @@ -0,0 +1,32 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.views; + +import software.aws.toolkits.eclipse.amazonq.configuration.DeprecationAcknowledgmentStore; +import software.aws.toolkits.eclipse.amazonq.configuration.PluginStore; +import software.aws.toolkits.eclipse.amazonq.configuration.PluginStoreKeys; + +final class ChatPromptAcknowledgmentHandler { + + static final String PAIR_PROGRAMMING_MESSAGE_ID = "programmerModeCardId"; + static final String DEPRECATION_NOTICE_MESSAGE_ID = "client-deprecation-notice"; + + private final PluginStore pluginStore; + private final DeprecationAcknowledgmentStore deprecationAcknowledgmentStore; + + ChatPromptAcknowledgmentHandler(final PluginStore pluginStore, + final DeprecationAcknowledgmentStore deprecationAcknowledgmentStore) { + this.pluginStore = pluginStore; + this.deprecationAcknowledgmentStore = deprecationAcknowledgmentStore; + } + + void acknowledge(final String messageId) { + if (PAIR_PROGRAMMING_MESSAGE_ID.equals(messageId)) { + pluginStore.put(PluginStoreKeys.PAIR_PROGRAMMING_ACKNOWLEDGED, "true"); + } else if (DEPRECATION_NOTICE_MESSAGE_ID.equals(messageId)) { + deprecationAcknowledgmentStore.acknowledge( + PluginStoreKeys.CHAT_DEPRECATION_NOTICE_ACKNOWLEDGED); + } + } +} diff --git a/plugin/tst/software/aws/toolkits/eclipse/amazonq/configuration/DeprecationAcknowledgmentStoreTest.java b/plugin/tst/software/aws/toolkits/eclipse/amazonq/configuration/DeprecationAcknowledgmentStoreTest.java new file mode 100644 index 000000000..3edf64584 --- /dev/null +++ b/plugin/tst/software/aws/toolkits/eclipse/amazonq/configuration/DeprecationAcknowledgmentStoreTest.java @@ -0,0 +1,100 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.configuration; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.internal.preferences.EclipsePreferences; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; +import org.junit.jupiter.api.Test; +import org.osgi.service.prefs.BackingStoreException; + +public final class DeprecationAcknowledgmentStoreTest { + + private static final String TEST_KEY = "testAcknowledgement"; + + @Test + void storesAcknowledgementInInstallationPreferences() { + var installationPreferences = new EclipsePreferences(); + var workspaceFallback = new InMemoryPluginStore(); + var store = new DeprecationAcknowledgmentStore( + installationPreferences, workspaceFallback, null); + + store.acknowledge(TEST_KEY); + + assertTrue(store.isAcknowledged(TEST_KEY)); + assertEquals("true", installationPreferences.get(TEST_KEY, null)); + assertFalse(workspaceFallback.contains(TEST_KEY)); + } + + @Test + void readsUnacknowledgedInstallationPreference() { + var store = new DeprecationAcknowledgmentStore( + new EclipsePreferences(), new InMemoryPluginStore(), null); + + assertFalse(store.isAcknowledged(TEST_KEY)); + } + + @Test + void fallsBackToWorkspaceWhenInstallationPreferencesAreUnavailable() { + var workspaceFallback = new InMemoryPluginStore(); + workspaceFallback.put(TEST_KEY, "true"); + var store = new DeprecationAcknowledgmentStore( + new UnwritablePreferences(), workspaceFallback, null); + + assertTrue(store.isAcknowledged(TEST_KEY)); + + store.acknowledge("anotherKey"); + assertEquals("true", workspaceFallback.get("anotherKey")); + } + + private static final class UnwritablePreferences extends EclipsePreferences { + @Override + public void flush() throws BackingStoreException { + throw new BackingStoreException("configuration scope is read-only"); + } + } + + private static final class InMemoryPluginStore implements PluginStore { + private final Map values = new HashMap<>(); + + @Override + public void put(final String key, final String value) { + values.put(key, value); + } + + @Override + public String get(final String key) { + return values.get(key); + } + + @Override + public void remove(final String key) { + values.remove(key); + } + + @Override + public void addChangeListener(final IPreferenceChangeListener prefChangeListener) { + } + + @Override + public void putObject(final String key, final T value) { + throw new UnsupportedOperationException(); + } + + @Override + public T getObject(final String key, final Class type) { + throw new UnsupportedOperationException(); + } + + boolean contains(final String key) { + return values.containsKey(key); + } + } +} diff --git a/plugin/tst/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatClientConfigurationTest.java b/plugin/tst/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatClientConfigurationTest.java new file mode 100644 index 000000000..c28fb6aef --- /dev/null +++ b/plugin/tst/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatClientConfigurationTest.java @@ -0,0 +1,82 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.providers.assets; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.internal.preferences.EclipsePreferences; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; +import org.junit.jupiter.api.Test; + +import software.aws.toolkits.eclipse.amazonq.configuration.DeprecationAcknowledgmentStore; +import software.aws.toolkits.eclipse.amazonq.configuration.PluginStore; +import software.aws.toolkits.eclipse.amazonq.configuration.PluginStoreKeys; + +public final class ChatClientConfigurationTest { + + @Test + void forwardsAcknowledgedStateForLegacyAndDeprecationPrompts() { + var pluginStore = new InMemoryPluginStore(); + pluginStore.put(PluginStoreKeys.CHAT_DISCLAIMER_ACKNOWLEDGED, "true"); + pluginStore.put(PluginStoreKeys.PAIR_PROGRAMMING_ACKNOWLEDGED, "true"); + var deprecationStore = new DeprecationAcknowledgmentStore( + new EclipsePreferences(), pluginStore, null); + deprecationStore.acknowledge(PluginStoreKeys.CHAT_DEPRECATION_NOTICE_ACKNOWLEDGED); + + String javaScript = ChatClientConfiguration.load(pluginStore, deprecationStore) + .toJavaScript(); + + assertTrue(javaScript.contains("disclaimerAcknowledged: true")); + assertTrue(javaScript.contains("pairProgrammingAcknowledged: true")); + assertTrue(javaScript.contains("deprecationNoticeAcknowledged: true")); + } + + @Test + void forwardsFalseForUnacknowledgedDeprecationPrompt() { + var pluginStore = new InMemoryPluginStore(); + var deprecationStore = new DeprecationAcknowledgmentStore( + new EclipsePreferences(), pluginStore, null); + + String javaScript = ChatClientConfiguration.load(pluginStore, deprecationStore) + .toJavaScript(); + + assertTrue(javaScript.contains("deprecationNoticeAcknowledged: false")); + } + + private static final class InMemoryPluginStore implements PluginStore { + private final Map values = new HashMap<>(); + + @Override + public void put(final String key, final String value) { + values.put(key, value); + } + + @Override + public String get(final String key) { + return values.get(key); + } + + @Override + public void remove(final String key) { + values.remove(key); + } + + @Override + public void addChangeListener(final IPreferenceChangeListener prefChangeListener) { + } + + @Override + public void putObject(final String key, final T value) { + throw new UnsupportedOperationException(); + } + + @Override + public T getObject(final String key, final Class type) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/plugin/tst/software/aws/toolkits/eclipse/amazonq/views/ChatPromptAcknowledgmentHandlerTest.java b/plugin/tst/software/aws/toolkits/eclipse/amazonq/views/ChatPromptAcknowledgmentHandlerTest.java new file mode 100644 index 000000000..f4fddc433 --- /dev/null +++ b/plugin/tst/software/aws/toolkits/eclipse/amazonq/views/ChatPromptAcknowledgmentHandlerTest.java @@ -0,0 +1,97 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.views; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.internal.preferences.EclipsePreferences; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import software.aws.toolkits.eclipse.amazonq.configuration.DeprecationAcknowledgmentStore; +import software.aws.toolkits.eclipse.amazonq.configuration.PluginStore; +import software.aws.toolkits.eclipse.amazonq.configuration.PluginStoreKeys; + +public final class ChatPromptAcknowledgmentHandlerTest { + + private InMemoryPluginStore pluginStore; + private DeprecationAcknowledgmentStore deprecationStore; + private ChatPromptAcknowledgmentHandler handler; + + @BeforeEach + void setUp() { + pluginStore = new InMemoryPluginStore(); + deprecationStore = new DeprecationAcknowledgmentStore( + new EclipsePreferences(), pluginStore, null); + handler = new ChatPromptAcknowledgmentHandler(pluginStore, deprecationStore); + } + + @Test + void persistsDeprecationNoticeAcknowledgement() { + handler.acknowledge(ChatPromptAcknowledgmentHandler.DEPRECATION_NOTICE_MESSAGE_ID); + + assertTrue(deprecationStore.isAcknowledged( + PluginStoreKeys.CHAT_DEPRECATION_NOTICE_ACKNOWLEDGED)); + } + + @Test + void retainsPairProgrammingAcknowledgementHandling() { + handler.acknowledge(ChatPromptAcknowledgmentHandler.PAIR_PROGRAMMING_MESSAGE_ID); + + assertEquals("true", pluginStore.get(PluginStoreKeys.PAIR_PROGRAMMING_ACKNOWLEDGED)); + } + + @Test + void ignoresUnknownAndMissingMessageIds() { + handler.acknowledge("unknown"); + handler.acknowledge(null); + + assertFalse(pluginStore.contains(PluginStoreKeys.PAIR_PROGRAMMING_ACKNOWLEDGED)); + assertFalse(deprecationStore.isAcknowledged( + PluginStoreKeys.CHAT_DEPRECATION_NOTICE_ACKNOWLEDGED)); + } + + private static final class InMemoryPluginStore implements PluginStore { + private final Map values = new HashMap<>(); + + @Override + public void put(final String key, final String value) { + values.put(key, value); + } + + @Override + public String get(final String key) { + return values.get(key); + } + + @Override + public void remove(final String key) { + values.remove(key); + } + + @Override + public void addChangeListener(final IPreferenceChangeListener prefChangeListener) { + } + + @Override + public void putObject(final String key, final T value) { + throw new UnsupportedOperationException(); + } + + @Override + public T getObject(final String key, final Class type) { + throw new UnsupportedOperationException(); + } + + boolean contains(final String key) { + return values.containsKey(key); + } + } +}