CookieStoreLegacyMapTestCase.java
/*
* Copyright The Undertow Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.undertow.server;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import io.undertow.server.handlers.Cookie;
import io.undertow.server.handlers.CookieImpl;
import io.undertow.testutils.category.UnitTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;
/**
* Tests for the deprecated {@link CookieStore#asLegacyMap()} functionality. This ensures backward compatibility with
* the deprecated {@link HttpServerExchange#getRequestCookies()} and getResponseCookies() methods.
*
* @author <a href="mailto:jperkins@ibm.com">James R. Perkins</a>
*/
@SuppressWarnings({"removal", "ConstantValue", "RedundantCollectionOperation"})
@Category(UnitTest.class)
public class CookieStoreLegacyMapTestCase {
/**
* Tests that the legacy flattened map implements the {@link Map#values()} correctly.
*/
@Test
public void valuesIteration() {
final CookieStore store = new CookieStore();
store.add(new CookieImpl("SESSION", "abc123"));
store.add(new CookieImpl("TRACKING", "xyz789"));
store.add(new CookieImpl("LANG", "en"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
// This threw UnsupportedOperationException in Beta1
final Collection<Cookie> values = legacyMap.values();
Assert.assertNotNull("values() should not return null", values);
Assert.assertEquals("Should have 3 cookies", 3, values.size());
// Verify we can iterate
int count = 0;
for (Cookie cookie : values) {
Assert.assertNotNull("Cookie should not be null", cookie);
count++;
}
Assert.assertEquals("Should iterate over all cookies", 3, count);
}
/**
* Test that the legacy flattened map implements the {@link Map#entrySet()} correctly.
*/
@Test
public void entrySetIteration() {
final CookieStore store = new CookieStore();
store.add(new CookieImpl("FOO", "value1"));
store.add(new CookieImpl("BAR", "value2"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
final Set<Map.Entry<String, Cookie>> entries = legacyMap.entrySet();
Assert.assertNotNull("entrySet() should not return null", entries);
Assert.assertEquals("Should have 2 entries", 2, entries.size());
// Verify iteration
int count = 0;
for (Map.Entry<String, Cookie> entry : entries) {
Assert.assertNotNull("Entry key should not be null", entry.getKey());
Assert.assertNotNull("Entry value should not be null", entry.getValue());
count++;
}
Assert.assertEquals("Should iterate over all entries", 2, count);
}
/**
* Tests that the legacy map will return the first cookie on a duplicate cookie name, but different paths. This
* is for RFC-2109.
*/
@Test
public void multipleRFC2109Cookies() {
final CookieStore store = new CookieStore();
final Cookie first = new CookieImpl("CUSTOMER", "JOE").setPath("/acme");
final Cookie second = new CookieImpl("CUSTOMER", "MONICA").setPath("/");
store.add(first);
store.add(second);
store.add(new CookieImpl("SESSION", "abc"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
// We should only have one entry as the flattened map should remove the second entry added
Assert.assertEquals("Should have 2 cookies", 2, legacyMap.size());
final Cookie result = legacyMap.get("CUSTOMER");
Assert.assertNotNull("Should find CUSTOMER cookie", result);
// Should return the last one added
Assert.assertEquals("Should return last cookie", "MONICA", result.getValue());
Assert.assertEquals("Should have correct path", "/", result.getPath());
Assert.assertEquals("Values should have 2 entries", 2, legacyMap.values().size());
Assert.assertEquals("EntrySet should have 2 entries", 2, legacyMap.entrySet().size());
}
/**
* Test that duplicate cookies (same name+path+domain) are replaced in the flat map.
*/
@Test
public void duplicateRFC6265Cookies() {
final CookieStore store = new CookieStore();
store.add(new CookieImpl("SESSION", "first"));
store.add(new CookieImpl("SESSION", "second")); // Same name+path+domain - replaces
store.add(new CookieImpl("SESSION", "third")); // Replaces again
final Map<String, Cookie> legacyMap = store.asLegacyMap();
// We should only have one entry as the flattened map should remove the second entry added
Assert.assertEquals("Should have 1 cookies", 1, legacyMap.size());
Cookie result = legacyMap.get("SESSION");
Assert.assertNotNull("Should find SESSION cookie", result);
Assert.assertEquals("Should have the last value", "third", result.getValue());
}
/**
* Test {@link Map#put(Object, Object)} replaces all cookies with that name.
*/
@Test
public void putReplacesAllCookies() {
final CookieStore store = new CookieStore();
store.add(new CookieImpl("FOO", "original").setPath("/a"));
store.add(new CookieImpl("FOO", "another").setPath("/b"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
final Cookie newCookie = new CookieImpl("FOO", "replacement");
final Cookie old = legacyMap.put("FOO", newCookie);
Assert.assertNotNull("put() should return old value", old);
Assert.assertEquals("Should return last old cookie", "another", old.getValue());
// After put, only the new cookie should exist
final Cookie result = legacyMap.get("FOO");
Assert.assertEquals("Should have new value", "replacement", result.getValue());
// Verify only one FOO cookie remains in the store
Assert.assertEquals("Store should have only one cookie", 1, store.get("FOO").size());
}
/**
* Test {@link Map#remove(Object)} removes all cookies with that name.
*/
@Test
public void removeRemovesAllCookies() {
final CookieStore store = new CookieStore();
store.add(new CookieImpl("BAR", "value1").setPath("/x"));
store.add(new CookieImpl("OTHER", "keep"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
final Cookie removed = legacyMap.remove("BAR");
Assert.assertNotNull("remove() should return removed value", removed);
Assert.assertEquals("Should return first cookie", "value1", removed.getValue());
Assert.assertNull("BAR should be gone", legacyMap.get("BAR"));
Assert.assertEquals("OTHER should remain", "keep", legacyMap.get("OTHER").getValue());
// Verify store is updated
Assert.assertTrue("Store should not have BAR cookies", store.get("BAR").isEmpty());
}
/**
* Test {@link Map#containsKey(Object)} works correctly.
*/
@Test
public void containsKey() {
final CookieStore store = new CookieStore();
store.add(new CookieImpl("EXISTS", "value"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
Assert.assertTrue("Should contain EXISTS", legacyMap.containsKey("EXISTS"));
Assert.assertFalse("Should not contain MISSING", legacyMap.containsKey("MISSING"));
}
/**
* Test {@link Map#containsValue(Object)} works correctly.
*/
@Test
public void containsValue() {
final CookieStore store = new CookieStore();
final Cookie cookie = new CookieImpl("TEST", "value");
store.add(cookie);
store.add(new CookieImpl("OTHER", "different"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
// Note: containsValue checks object equality, not just the value string
Assert.assertTrue("Should contain the TEST cookie",
legacyMap.containsValue(new CookieImpl("TEST", "value")));
Assert.assertFalse("Should not contain non-existent cookie",
legacyMap.containsValue(new CookieImpl("FAKE", "fake")));
}
/**
* Test {@link Map#size()} and {@link Map#isEmpty()} work correctly.
*/
@Test
public void sizeAndEmpty() {
final CookieStore store = new CookieStore();
final Map<String, Cookie> legacyMap = store.asLegacyMap();
Assert.assertTrue("New map should be empty", legacyMap.isEmpty());
Assert.assertEquals("New map should have size 0", 0, legacyMap.size());
store.add(new CookieImpl("A", "1"));
store.add(new CookieImpl("B", "2"));
Assert.assertFalse("Map should not be empty", legacyMap.isEmpty());
Assert.assertEquals("Map should have size 2", 2, legacyMap.size());
}
/**
* Test {@link Map#clear()} removes all cookies.
*/
@Test
public void clear() {
final CookieStore store = new CookieStore();
store.add(new CookieImpl("A", "1"));
store.add(new CookieImpl("B", "2"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
Assert.assertEquals("Should have 2 cookies", 2, legacyMap.size());
legacyMap.clear();
Assert.assertTrue("Map should be empty", legacyMap.isEmpty());
Assert.assertTrue("Store should be empty", store.isEmpty());
}
/**
* Test {@link Map#putAll(Map)}} works correctly.
*/
@Test
public void putAll() {
final CookieStore store = new CookieStore();
final Map<String, Cookie> legacyMap = store.asLegacyMap();
final Map<String, Cookie> toAdd = Map.of(
"COOKIE1", new CookieImpl("COOKIE1", "value1"),
"COOKIE2", new CookieImpl("COOKIE2", "value2")
);
legacyMap.putAll(toAdd);
Assert.assertEquals("Should have 2 cookies", 2, legacyMap.size());
Assert.assertEquals("COOKIE1 should exist", "value1", legacyMap.get("COOKIE1").getValue());
Assert.assertEquals("COOKIE2 should exist", "value2", legacyMap.get("COOKIE2").getValue());
}
/**
* Test {@link Map#keySet()} returns cookie names.
*/
@Test
public void keySet() {
final CookieStore store = new CookieStore();
store.add(new CookieImpl("FOO", "1"));
store.add(new CookieImpl("BAR", "2"));
final Map<String, Cookie> legacyMap = store.asLegacyMap();
final Set<String> keys = legacyMap.keySet();
Assert.assertEquals("Should have 2 keys", 2, keys.size());
Assert.assertTrue("Should contain FOO", keys.contains("FOO"));
Assert.assertTrue("Should contain BAR", keys.contains("BAR"));
}
/**
* Test that the legacy map reflects changes to the underlying store.
*/
@Test
public void mutability() {
final CookieStore store = new CookieStore();
final Map<String, Cookie> legacyMap = store.asLegacyMap();
Assert.assertTrue("Should start empty", legacyMap.isEmpty());
// Add to store
store.add(new CookieImpl("NEW", "cookie"));
Assert.assertFalse("Should not be empty", legacyMap.isEmpty());
Assert.assertEquals("Should see new cookie", "cookie", legacyMap.get("NEW").getValue());
Assert.assertEquals("The store should have the cookie", "cookie", store.get("NEW").get(0).getValue());
}
}