\set ECHO none BEGIN; \i test/pgtap-core.sql CREATE EXTENSION semver; SELECT plan(334); --SELECT * FROM no_plan(); SELECT has_type('semver'); SELECT is( NULL::semver, NULL, 'semvers should be NULLable' ); SELECT lives_ok( $$ SELECT '$$ || v || $$'::semver $$, '"' || v || '" is a valid semver' ) FROM unnest(ARRAY[ '1.2.2', '0.2.2', '0.0.0', '0.1.999', '9999.9999999.823823', '1.0.0-beta1', '1.0.0-beta2', '1.0.0', '1.0.0-1', '1.0.0-alpha+d34dm34t', '1.0.0+d34dm34t', '20110204.0.0', '1.0.0-alpha.0a', '1.0.0+010', '1.0.0+alpha.010', '1.0.0-0AEF' ]) AS v; SELECT throws_ok( $$ SELECT '$$ || v || $$'::semver $$, NULL, '"' || v || '" is not a valid semver' ) FROM unnest(ARRAY[ '1.2', '1.2.02', '1.2.2-', '1.2.3b#5', '03.3.3', 'v1.2.2', '1.3b', '1.4b.0', '1v', '1v.2.2v', '1.2.4b.5', '1.0.0-alpha.010', '1.0.0-02799', '1.1.2+.123', '1.1.2-.123', '1.2.3-ñø', '1.2.3+ñø1' ]) AS v; -- Test =, <=, and >=. SELECT collect_tap(ARRAY[ ok(semver_cmp(lv::semver, rv::semver) = 0, 'semver(' || lv || ', ' || rv || ') should = 0'), ok(lv::semver = rv::semver, 'v' || lv || ' should = v' || rv), ok(lv::semver <= rv::semver, 'v' || lv || ' should be <= v' || rv), ok(lv::semver >= rv::semver, 'v' || lv || ' should be >= v' || rv) ]) FROM (VALUES ('1.2.2', '1.2.2'), ('1.2.23', '1.2.23'), ('0.0.0', '0.0.0'), ('999.888.7777', '999.888.7777'), ('0.1.2-beta3', '0.1.2-beta3') ) AS f(lv, rv); -- Test semver <> semver SELECT collect_tap(ARRAY[ ok(semver_cmp(lv::semver, rv::semver) <> 0, 'semver(' || lv || ', ' || rv || ') should <> 0'), ok(lv::semver <> rv::semver, 'v' || lv || ' should not equal v' || rv) ]) FROM (VALUES ('1.2.2', '1.2.3'), ('0.0.1', '1.0.0'), ('1.0.1', '1.1.0'), ('1.1.1', '1.1.0'), ('1.2.3-b', '1.2.3'), ('1.2.3', '1.2.3-b'), ('1.2.3-a', '1.2.3-b'), ('1.2.3-aaaaaaa1', '1.2.3-aaaaaaa2'), ('1.2.3-1.2.3', '1.2.3-1.2.3.4') ) AS f(lv, rv); -- Test >, >=, <, and <=. SELECT collect_tap(ARRAY[ ok( semver_cmp(lv::semver, rv::semver) > 0, 'semver(' || lv || ', ' || rv || ') should > 0'), ok( semver_cmp(rv::semver, lv::semver) < 0, 'semver(' || rv || ', ' || lv || ') should < 0'), ok(lv::semver > rv::semver, 'v' || lv || ' should be > v' || rv), ok(lv::semver >= rv::semver, 'v' || lv || ' should be >= v' || rv), ok(rv::semver < lv::semver, 'v' || rv || ' should be < v' || lv), ok(rv::semver <= lv::semver, 'v' || rv || ' should be <= v' || lv) ]) FROM (VALUES ('2.2.2', '1.1.1'), ('2.2.2', '2.1.1'), ('2.2.2', '2.2.1'), ('2.2.2-b', '2.2.1'), ('2.2.2', '2.2.2-b'), ('2.2.2-c', '2.2.2-b'), ('2.2.2-rc-2', '2.2.2-RC-1'), ('2.2.2-rc-1', '2.2.2-RC-1'), ('0.9.10', '0.9.9'), ('1.0.1-1.2.3', '1.0.1-0.9.9.9') ) AS f(lv, rv); -- Test to_semver(). SELECT has_function('to_semver'); SELECT has_function('to_semver', ARRAY['text']); SELECT function_returns('to_semver', 'semver'); SELECT is( to_semver(dirty), clean::semver, 'to_semver(' || dirty || ') should return ' || clean ) FROM (VALUES ('1.2.2', '1.2.2'), ('01.2.2', '1.2.2'), ('1.02.2', '1.2.2'), ('1.2.02', '1.2.2'), ('1.2.02b', '1.2.2-b'), ('1.2.02beta-3 ', '1.2.2-beta-3'), ('1.02.02rc1', '1.2.2-rc1'), ('1.0', '1.0.0'), ('1', '1.0.0'), ('.0.02', '0.0.2'), ('1..02', '1.0.2'), ('1..', '1.0.0'), ('1.1', '1.1.0'), ('1.2.b1', '1.2.0-b1'), ('9.0beta4', '9.0.0-beta4'), -- PostgreSQL format. ('9b', '9.0.0-b'), ('rc1', '0.0.0-rc1'), ('', '0.0.0'), ('..2', '0.0.2'), ('1.2.3 a', '1.2.3-a'), ('..2 b', '0.0.2-b'), (' 012.2.2', '12.2.2'), ('20110204', '20110204.0.0') ) v(dirty, clean); SELECT is( to_semver(clean), clean::semver, 'to_semver(' || clean || ') should return incoming text' ) FROM (VALUES ('1.0.0-alpha'), -- SV2 9. ('1.0.0-alpha.1'), -- SV2 9. ('1.0.0-0.3.7'), -- SV2 9. ('1.0.0-x.7.z.92'), -- SV2 9. ('1.0.0-alpha+001'), -- SV2 10. ('1.0.0+20130313144700'), -- SV2 10. ('1.0.0-beta+exp.sha.5114f85') -- SV2 10. ) v(clean); -- to_semver still needs to reject truly bad input SELECT throws_ok( $$ SELECT '$$ || v || $$'::semver $$, NULL, '"' || v || '" is not a valid semver' ) FROM unnest(ARRAY[ '1.2.0 beta 4', '1.2.2-', '1.2.3b#5', 'v1.2.2', '1.4b.0', '1v.2.2v', '1.2.4b.5', '1.2.3.4', '1.2.3 4', '1.2000000000000000.3.4' ]) AS v; -- Test sort ordering CREATE TABLE vs ( version semver ); INSERT INTO vs VALUES ('1.2.0'), ('1.0.0'), ('1.0.0-p0'), ('0.9.9'), ('0.9.10'); SELECT is(max(version), '1.2.0', 'max(semver) should work') FROM vs; SELECT is(min(version), '0.9.9', 'min(semver) should work') FROM vs; SELECT results_eq( $$ SELECT version FROM vs ORDER BY version USING < $$, $$ VALUES ('0.9.9'::semver), ('0.9.10'::semver), ('1.0.0-p0'::semver), ('1.0.0'::semver), ('1.2.0'::semver) $$, 'ORDER BY semver USING < should work' ); SELECT results_eq( $$ SELECT version FROM vs ORDER BY version USING > $$, $$ VALUES ('1.2.0'::semver), ('1.0.0'::semver), ('1.0.0-p0'::semver), ('0.9.10'::semver), ('0.9.9'::semver) $$, 'ORDER BY semver USING > should work' ); -- Test constructors. SELECT is( text('1.2.0'::semver), '1.2.0', 'construct to text' ); SELECT is( semver('1.2.0'), '1.2.0'::semver, 'construct from text' ); SELECT is( semver(1.2), '1.2.0'::semver, 'construct from bare number' ); SELECT is( semver(1.2::numeric), '1.2.0'::semver, 'construct from numeric' ); SELECT is( semver(1), '1.0.0'::semver, 'construct from bare integer' ); SELECT is( semver(1::integer), '1.0.0'::semver, 'construct from integer' ); SELECT is( semver(1::bigint), '1.0.0'::semver, 'construct from bigint' ); SELECT is( semver(1::smallint), '1.0.0'::semver, 'construct from smallint' ); SELECT is( semver(1.2::decimal), '1.2.0'::semver, 'construct from decimal' ); SELECT is( semver(1.2::real), '1.2.0'::semver, 'construct from real' ); SELECT is( semver(1.2::double precision), '1.2.0'::semver, 'construct from double' ); SELECT is( semver(1.2::float), '1.2.0'::semver, 'construct from float' ); -- Test casting. SELECT is( semver('1.2.0'::text), '1.2.0', 'cast to text' ); SELECT is( text('1.2.0')::semver, '1.2.0'::semver, 'cast from text' ); SELECT is( 1::semver, '1.0.0'::semver, 'Cast from bare integer'); SELECT is( 1.2::semver, '1.2.0'::semver, 'Cast from bare number'); SELECT is( 1.2::numeric::semver, '1.2.0'::semver, 'Cast from numeric'); SELECT is( 1::integer::semver, '1.0.0'::semver, 'Cast from integer'); SELECT is( 1::bigint::semver, '1.0.0'::semver, 'Cast from bigint'); SELECT is( 1::smallint::semver, '1.0.0'::semver, 'Cast from smallint'); SELECT is( 1.0::decimal::semver, '1.0.0'::semver, 'Cast from decimal'); SELECT is( 1::decimal::semver, '1.0.0'::semver, 'Cast from decimal'); SELECT is( 1.0::real::semver, '1.0.0'::semver, 'Cast from real'); SELECT is( 1.0::double precision::semver, '1.0.0'::semver, 'Cast from double precision'); SELECT is( 1.0::float::semver, '1.0.0'::semver, 'Cast from float'); -- Test casting some more. SELECT IS(lv::text, rv, 'Should correctly cast "' || rv || '" to text') FROM (VALUES ('1.0.0-beta'::semver, '1.0.0-beta'), ('1.0.0-beta1'::semver, '1.0.0-beta1'), ('1.0.0-alpha'::semver, '1.0.0-alpha'), ('1.0.0-alph'::semver, '1.0.0-alph'), ('1.0.0-food'::semver, '1.0.0-food'), ('1.0.0-f111'::semver, '1.0.0-f111'), ('1.0.0-f111asbcdasdfasdfasdfasdfasdfasdffasdfadsf'::semver, '1.0.0-f111asbcdasdfasdfasdfasdfasdfasdffasdfadsf') ) AS f(lv, rv); -- SEMV 2.0.0 tests. SELECT lives_ok( $$ SELECT '$$ || v || $$'::semver $$, '"' || v || '" is a valid 2.0.0 semver' ) FROM unnest(ARRAY[ '1.0.0+1', '1.0.0-1+1', '1.0.0-1.1+1', '1.0.0-1.1.1.1.1.1.1.1.1.1.1+1.1.1.1.1.1.1.1', '1.0.0-1.2', '1.0.0-1.0.2', '1.0.0-alpha', '1.0.0-alpha.1', '1.0.0-0.3.7', '1.0.0-x.7.z.92', '0.2.13+1583426134.07de632' ]) AS v; SELECT throws_ok( $$ SELECT '$$ || v || $$'::semver $$, NULL, '"' || v || '" is not a valid 2.0.0 semver' ) FROM unnest(ARRAY[ '1.0.0-a..', '1.0.0-a.1.', '1.0.0+1_1', '1.0.0-1....', '1.0.0-1_2', '1.0.0-1.02' ]) AS v; DELETE FROM vs; INSERT INTO vs VALUES ('0.9.9-a1.1+1234'::semver), ('0.9.9-a1.2.3'::semver), ('0.9.9-a1.2'::semver), ('0.9.9'::semver), ('1.0.0+99'::semver), ('1.0.0-1'::semver); SELECT results_eq( $$ SELECT version FROM vs ORDER BY version USING < $$, $$ VALUES ('0.9.9-a1.1+1234'::semver), ('0.9.9-a1.2'::semver), ('0.9.9-a1.2.3'::semver), ('0.9.9'::semver), ('1.0.0-1'::semver), ('1.0.0+99'::semver) $$, 'ORDER BY semver (2.0.0) USING < should work' ); SELECT results_eq( $$ SELECT version FROM vs ORDER BY version USING > $$, $$ VALUES ('1.0.0+99'::semver), ('1.0.0-1'::semver), ('0.9.9'::semver), ('0.9.9-a1.2.3'::semver), ('0.9.9-a1.2'::semver), ('0.9.9-a1.1+1234'::semver) $$, 'ORDER BY semver (2.0.0) USING > should work' ); SELECT collect_tap(ARRAY[ ok(semver_cmp(lv::semver, rv::semver) = 0, 'semver(' || lv || ', ' || rv || ') should = 0'), ok(lv::semver = rv::semver, 'v' || lv || ' should = v' || rv), ok(lv::semver <= rv::semver, 'v' || lv || ' should be <= v' || rv), ok(lv::semver >= rv::semver, 'v' || lv || ' should be >= v' || rv) ]) FROM (VALUES ('1.0.0-1+1', '1.0.0-1+5'), ('1.0.0-1.1+1', '1.0.0-1.1+5') ) AS f(lv, rv); -- Regressions. SELECT is( to_semver(lv)::TEXT, rv, 'Should correctly represent "' || lv || '" as "' || rv || '"' ) FROM (VALUES ('0.5.0-release1', '0.5.0-release1'), ('0.5.0release1', '0.5.0-release1'), ('0.5-release1', '0.5.0-release1'), ('0.5release1', '0.5.0-release1'), ('0.5-1', '0.5.0-1'), ('1.2.3-1.02', '1.2.3-1.2') ) AS f(lv, rv); -- Test is_semver(). SELECT has_function('is_semver'); SELECT has_function('is_semver', ARRAY['text']); SELECT function_returns('is_semver', 'boolean'); SELECT is( is_semver(stimulus), expected, 'is_semver(' || stimulus || ') should return ' || expected::text ) FROM (VALUES ('1.2.2', true), ('0.2.2', true), ('0.0.0', true), ('0.1.999', true), ('9999.9999999.823823', true), ('1.0.0-beta1', true), ('1.0.0-beta2', true), ('1.0.0', true), ('1.0.0-1', true), ('1.0.0-alpha+d34dm34t', true), ('1.0.0+d34dm34t', true), ('20110204.0.0', true), ('1.2', false), ('1.2.02', false), ('1.2.2-', false), ('1.2.3b#5', false), ('03.3.3', false), ('v1.2.2', false), ('1.3b', false), ('1.4b.0', false), ('1v', false), ('1v.2.2v', false), ('1.2.4b.5', false), ('2016.5.18-MYW-600', true), ('1010.5.0+2016-05-27-1832', true), ('0.2.13+1583426134.07de632', true) ) v(stimulus, expected); -- issue-gh-23 SELECT lives_ok( $$ SELECT '$$ || v || $$'::semver $$, '"' || v || '" is a valid semver' ) FROM unnest(ARRAY[ '2.3.0+80' ]) AS v; SELECT is( to_semver(dirty), clean::semver, 'to_semver(' || dirty || ') should return ' || clean ) FROM (VALUES ('2.3.0+80', '2.3.0+80') ) v(dirty, clean); SELECT is(lv::text, rv, 'Should correctly cast "' || rv || '" to text') FROM (VALUES ('2.3.0+80'::semver, '2.3.0+80') ) AS f(lv, rv); SELECT isnt(lv::semver > rv::semver, true, '"' || lv || '" > "' || rv || '" (NOT!)') FROM (VALUES ('2.3.0+80', '2.3.0+110') ) AS f(lv, rv); SELECT is(lv::semver > rv::semver, true, '"' || lv || '" > "' || rv || '"') FROM (VALUES ('2.3.0+80', '2.3.0-alpha+110') ) AS f(lv, rv); CREATE TABLE vs23 ( version semver ); INSERT INTO vs23 VALUES ('1.0.0-alpha'), ('1.0.0-alpha.1'), ('1.0.0-alpha.beta'), ('1.0.0-beta'), ('1.0.0-beta.2'), ('1.0.0-beta.11'), ('1.0.0-rc.1'), ('1.0.0'); SELECT results_eq( $$ SELECT version FROM vs23 ORDER BY version USING < $$, $$ VALUES ('1.0.0-alpha'::semver), ('1.0.0-alpha.1'::semver), ('1.0.0-alpha.beta'::semver), ('1.0.0-beta'::semver), ('1.0.0-beta.2'::semver), ('1.0.0-beta.11'::semver), ('1.0.0-rc.1'::semver), ('1.0.0'::semver) $$, 'ORDER BY semver USING < should work (section 11)' ); SELECT results_eq( $$ SELECT version FROM vs23 ORDER BY version USING > $$, $$ VALUES ('1.0.0'::semver), ('1.0.0-rc.1'::semver), ('1.0.0-beta.11'::semver), ('1.0.0-beta.2'::semver), ('1.0.0-beta'::semver), ('1.0.0-alpha.beta'::semver), ('1.0.0-alpha.1'::semver), ('1.0.0-alpha'::semver) $$, 'ORDER BY semver USING > should work (section 11)' ); SELECT is(lv::semver = rv::semver, true, '"' || lv || '" = "' || rv || '"') FROM (VALUES ('1.0.0', '1.0.0+535') ) AS f(lv, rv); SELECT isnt(lv::semver < rv::semver, true, '"' || lv || '" < "' || rv || '" (NOT!)') FROM (VALUES ('1.0.0', '1.0.0+535') ) AS f(lv, rv); SELECT isnt(lv::semver > rv::semver, true, '"' || lv || '" > "' || rv || '" (NOT!)') FROM (VALUES ('1.0.0', '1.0.0+535') ) AS f(lv, rv); -- Test get_semver_major SELECT has_function('get_semver_major'); SELECT has_function('get_semver_major', 'semver'); SELECT function_returns('get_semver_major', 'integer'); SELECT is(get_semver_major('2.1.0-alpha'::semver), 2, 'major version check'); -- Test get_semver_minor SELECT has_function('get_semver_minor'); SELECT has_function('get_semver_minor', 'semver'); SELECT function_returns('get_semver_minor', 'integer'); SELECT is(get_semver_minor('2.1.0-alpha'::semver), 1, 'minor version check'); -- Test get_semver_patch SELECT has_function('get_semver_patch'); SELECT has_function('get_semver_patch', 'semver'); SELECT function_returns('get_semver_patch', 'integer'); SELECT is(get_semver_patch('2.1.0-alpha'::semver), 0, 'patch version check'); -- Test get_semver_prerelease SELECT has_function('get_semver_prerelease'); SELECT has_function('get_semver_prerelease', 'semver'); SELECT function_returns('get_semver_prerelease', 'text'); SELECT is(get_semver_prerelease('2.1.0-alpha'::semver), 'alpha', 'prerelease label check'); SELECT is(get_semver_prerelease('2.1.0-alpha+build'::semver), 'alpha', 'prerelease label check. must return prerelease only'); SELECT is(get_semver_prerelease('2.1.0+build'::semver), '', 'prerelease label check. must return empty string'); -- Test range type. SELECT ok( '1.0.0'::semver <@ '[1.0.0, 2.0.0]'::semverrange, '1.0.0 should be in range [1.0.0, 2.0.0]' ); SELECT ok( NOT '1.0.0'::semver <@ '[1.0.1, 2.0.0]'::semverrange, '1.0.0 should not be in range [1.0.1, 2.0.0]' ); SELECT ok( NOT semverrange('1.0.0', '2.0.0') @> '2.0.0'::semver, '2.0.0 should not be in range [1.0.1, 2.0.0)' ); SELECT ok( semverrange('1.0.0', '2.0.0') @> '1.9999.9999'::semver, '1.9999.9999 should be in range [1.0.1, 2.0.0)' ); SELECT ok( '1000.0.0'::semver <@ '[1.0.0,]'::semverrange, '1000.0.0 should be in range [1.0.0,)' ); SELECT bag_eq($$ SELECT version, version <@ ANY( '{"(1.0.0,1.2.3)", "(1.2.3,1.4.5)", "(1.4.5,2.0.0)"}'::semverrange[] ) AS valid FROM (VALUES ('1.0.0'::semver), ('1.0.1'), ('1.2.3'), ('1.2.4'), ('1.4.4'), ('1.4.5'), ('1.7.0'), ('2.0.0') ) AS v(version) $$, $$ VALUES ('1.0.0'::semver, false), ('1.0.1', true), ('1.2.3', false), ('1.2.4', true), ('1.4.4', true), ('1.4.5', false), ('1.7.0', true), ('2.0.0', false) $$, 'Should be able to work with arrays of semverranges'); -- Test formatting (issue-gh-48) SELECT is( '1.0.0+1234567890123456789012345'::semver::text, '1.0.0+1234567890123456789012345', 'Should properly format a 32 character semver' ); SELECT is( '1.0.0+12345678901234567890123456'::semver::text, '1.0.0+12345678901234567890123456', 'Should properly format a 33 character semver' ); -- issue-gh-46 SELECT is( '1.0.0-alpha-0'::semver::text, '1.0.0-alpha-0', 'Should propery format a prerelease with a hyphen' ); -- Make sure hashing works by forcing a hash aggregation. Also confirms -- that prerelease strings are compared case-sensitively. SET enable_sort=false; SELECT bag_eq( $$ SELECT DISTINCT v FROM (VALUES ('1.2.1'::SEMVER), ('1.2.1-RC1'), ('2.0.0-alph'), ('2.0.0-ALPH'), ('1.2.1'), ('9.20.30-OMG-1.23') ) v(v) $$, $$ (VALUES ('1.2.1'::SEMVER), ('1.2.1-RC1'), ('2.0.0-alph'), ('2.0.0-ALPH'), ('9.20.30-OMG-1.23') ) $$, 'Should get distinct values via hash aggregation' ); -- Test send and receive. SELECT has_function('semver_send'); SELECT has_function('semver_send', ARRAY['semver']); SELECT function_returns('semver_send', 'bytea'); SELECT has_function('semver_recv'); SELECT has_function('semver_recv', ARRAY['internal']); SELECT function_returns('semver_recv', 'semver'); -- Set up some values to copy. INSERT INTO vs VALUES ('1.2.2'::semver), ('9999.9999999.823823'), ('1.0.0-beta1'), ('1.0.0-1'), ('1.0.0-alpha+d34dm34t'), ('1.0.0+d34dm34t'), ('20110204.0.0'), ('1.0.0-0AEF'), (NULL) ; -- Test semver_send(). SELECT is( semver_send(version), '\x01' || version::bytea, 'semver_send(' || COALESCE(quote_literal(version::text), 'NULL') || ')' ) FROM vs; -- Cannot test semver_recv() directly, so instead copy to a file and back into -- another table. CREATE TABLE vs_recv(version semver); \copy vs TO semver_binary_copy.bin WITH BINARY; \copy vs_recv from semver_binary_copy.bin with binary; SELECT bag_eq( 'SELECT * FROM vs', 'SELECT * FROM vs_recv', 'Should have binary copied all of the semvers' ); SELECT * FROM finish(); ROLLBACK;