Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,33 @@ public void initializeBody() {

public String bodyParametersToJson(List<NameValuePair> bodyParameters) {
JsonObject jsonObject = new JsonObject();
JsonElement jsonElement;

// Parameters that should be parsed as their original types (not strings)
// These are parameters that accept Integer, Boolean, JsonArray, or JsonObject types
java.util.Set<String> numericAndStructuredParams = java.util.Set.of(
"collaborative", "device_ids", "ids", "insert_before", "offset",
"play", "position", "position_ms", "public", "range_length",
"range_start", "tracks", "uris"
);

for (NameValuePair nameValuePair : bodyParameters) {
try {
jsonElement = JsonParser.parseString(nameValuePair.getValue());
} catch (JsonSyntaxException e) {
jsonElement = new JsonPrimitive(nameValuePair.getValue());
String name = nameValuePair.getName();
String value = nameValuePair.getValue();

if (numericAndStructuredParams.contains(name)) {
// For known numeric/boolean/structured parameters, parse as JSON to preserve type
try {
JsonElement jsonElement = JsonParser.parseString(value);
jsonObject.add(name, jsonElement);
} catch (JsonSyntaxException e) {
// Fallback to string if parsing fails
jsonObject.addProperty(name, value);
}
} else {
// For string parameters (like name, description), always keep as string
// This prevents numeric strings like "2025" from being converted to numbers
jsonObject.addProperty(name, value);
}

jsonObject.add(nameValuePair.getName(), jsonElement);
}

return jsonObject.toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package se.michaelthelin.spotify.requests.data.player;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.junit.jupiter.api.Test;
import se.michaelthelin.spotify.SpotifyApi;

import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Test for ensuring numeric and structured parameters are preserved with correct types in JSON request bodies
*/
public class StartResumeUsersPlaybackRequestNumericTest {

@Test
public void shouldPreserveNumericParametersAsNumbers() {
// Create a request with numeric position_ms parameter
StartResumeUsersPlaybackRequest request = new SpotifyApi.Builder()
.setAccessToken("test-token")
.build()
.startResumeUsersPlayback()
.position_ms(10000)
.build();

// Generate the JSON body
String json = request.bodyParametersToJson(request.getBodyParameters());

// The JSON should contain unquoted number, not a string
assertTrue(json.contains("\"position_ms\":10000"),
"position_ms should be an unquoted number. Actual JSON: " + json);

// Should NOT contain quoted string
assertTrue(!json.contains("\"position_ms\":\"10000\""),
"position_ms should not be a quoted string. Actual JSON: " + json);
}

@Test
public void shouldPreserveBooleanParametersAsBooleans() {
// Create a request with JSON array uris
JsonArray uris = new JsonArray();
uris.add("spotify:track:test1");
uris.add("spotify:track:test2");

StartResumeUsersPlaybackRequest request = new SpotifyApi.Builder()
.setAccessToken("test-token")
.build()
.startResumeUsersPlayback()
.uris(uris)
.build();

// Generate the JSON body
String json = request.bodyParametersToJson(request.getBodyParameters());

// The JSON should contain a proper JSON array
assertTrue(json.contains("\"uris\":["),
"uris should be a JSON array. Actual JSON: " + json);
assertTrue(json.contains("spotify:track:test1"),
"uris should contain the track URIs. Actual JSON: " + json);
}

@Test
public void shouldPreserveJsonObjectParameters() {
// Create a request with JSON object offset
JsonObject offset = new JsonObject();
offset.addProperty("position", 5);

StartResumeUsersPlaybackRequest request = new SpotifyApi.Builder()
.setAccessToken("test-token")
.build()
.startResumeUsersPlayback()
.offset(offset)
.build();

// Generate the JSON body
String json = request.bodyParametersToJson(request.getBodyParameters());

// The JSON should contain a proper JSON object
assertTrue(json.contains("\"offset\":{"),
"offset should be a JSON object. Actual JSON: " + json);
assertTrue(json.contains("\"position\":5"),
"offset should contain the position property. Actual JSON: " + json);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package se.michaelthelin.spotify.requests.data.playlists;

import org.junit.jupiter.api.Test;
import se.michaelthelin.spotify.SpotifyApi;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Test for ensuring numeric strings are preserved as strings in JSON request bodies
*/
public class CreatePlaylistRequestNumericStringTest {

@Test
public void shouldPreserveNumericStringsAsStrings() {
// Create a request with numeric strings for name and description
CreatePlaylistRequest request = new SpotifyApi.Builder()
.setAccessToken("test-token")
.build()
.createPlaylist("testuser", "2025")
.description("2025")
.build();

// Generate the JSON body
String json = request.bodyParametersToJson(request.getBodyParameters());

// The JSON should contain quoted strings, not numbers
assertTrue(json.contains("\"name\":\"2025\""),
"Name should be a quoted string, not a number. Actual JSON: " + json);
assertTrue(json.contains("\"description\":\"2025\""),
"Description should be a quoted string, not a number. Actual JSON: " + json);

// Should NOT contain unquoted numbers
assertTrue(!json.contains("\"name\":2025"),
"Name should not be an unquoted number. Actual JSON: " + json);
assertTrue(!json.contains("\"description\":2025"),
"Description should not be an unquoted number. Actual JSON: " + json);
}

@Test
public void shouldPreserveMixedTypesCorrectly() {
// Test various edge cases
CreatePlaylistRequest request = new SpotifyApi.Builder()
.setAccessToken("test-token")
.build()
.createPlaylist("testuser", "123.45") // decimal number as string
.description("abc123") // mixed alphanumeric
.build();

String json = request.bodyParametersToJson(request.getBodyParameters());

// Both should remain as strings
assertTrue(json.contains("\"name\":\"123.45\""),
"Decimal number string should remain quoted. Actual JSON: " + json);
assertTrue(json.contains("\"description\":\"abc123\""),
"Mixed alphanumeric should remain quoted. Actual JSON: " + json);
}

@Test
public void shouldPreserveEmptyAndNullLikeStrings() {
CreatePlaylistRequest request = new SpotifyApi.Builder()
.setAccessToken("test-token")
.build()
.createPlaylist("testuser", "null")
.description("true")
.build();

String json = request.bodyParametersToJson(request.getBodyParameters());

// Should remain as quoted strings, not become null/boolean literals
assertTrue(json.contains("\"name\":\"null\""),
"String 'null' should remain quoted. Actual JSON: " + json);
assertTrue(json.contains("\"description\":\"true\""),
"String 'true' should remain quoted. Actual JSON: " + json);
}
}
Loading