attribute-syntax.test.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { AssociationPath, Attribute, sql } from '@sequelize/core';
  2. import { Unquote } from '@sequelize/core/_non-semver-use-at-your-own-risk_/expression-builders/dialect-aware-fn.js';
  3. import {
  4. parseAttributeSyntax,
  5. parseNestedJsonKeySyntax,
  6. } from '@sequelize/core/_non-semver-use-at-your-own-risk_/utils/attribute-syntax.js';
  7. import { expect } from 'chai';
  8. describe('parseAttributeSyntax', () => {
  9. it('parses simple attributes', () => {
  10. expect(parseAttributeSyntax('foo')).to.deep.eq(new Attribute('foo'));
  11. });
  12. it('parses simple associations', () => {
  13. expect(parseAttributeSyntax('$bar$')).to.deep.eq(new Attribute('bar'));
  14. expect(parseAttributeSyntax('$foo.bar$')).to.deep.eq(new AssociationPath(['foo'], 'bar'));
  15. expect(parseAttributeSyntax('$foo.zzz.bar$')).to.deep.eq(
  16. new AssociationPath(['foo', 'zzz'], 'bar'),
  17. );
  18. });
  19. it('throws for unbalanced association syntax', () => {
  20. // The error points at the erroneous character each time, but we only test the first one
  21. expect(() => parseAttributeSyntax('foo$')).to
  22. .throwWithCause(`Failed to parse syntax of attribute. Parse error at index 3:
  23. foo$
  24. ^`);
  25. expect(() => parseAttributeSyntax('$foo')).to
  26. .throwWithCause(`Failed to parse syntax of attribute. Parse error at index 4:
  27. $foo
  28. ^`);
  29. });
  30. it('parses cast syntax', () => {
  31. expect(parseAttributeSyntax('foo::bar')).to.deep.eq(sql.cast(new Attribute('foo'), 'bar'));
  32. });
  33. it('parses consecutive casts', () => {
  34. expect(parseAttributeSyntax('foo::bar::baz')).to.deep.eq(
  35. sql.cast(sql.cast(new Attribute('foo'), 'bar'), 'baz'),
  36. );
  37. });
  38. it('parses modifier syntax', () => {
  39. expect(parseAttributeSyntax('foo:unquote')).to.deep.eq(sql.unquote(new Attribute('foo')));
  40. });
  41. it('parses consecutive modifiers', () => {
  42. expect(parseAttributeSyntax('foo:unquote:unquote')).to.deep.eq(
  43. sql.unquote(sql.unquote(new Attribute('foo'))),
  44. );
  45. });
  46. it('parses casts and modifiers', () => {
  47. expect(parseAttributeSyntax('textAttr::json:unquote::integer')).to.deep.eq(
  48. sql.cast(sql.unquote(sql.cast(new Attribute('textAttr'), 'json')), 'integer'),
  49. );
  50. });
  51. it('treats everything after ::/: as a cast/modifier', () => {
  52. // "json.property" is treated as a cast, not a JSON path
  53. // but it's not a valid cast, so it will throw
  54. expect(() => parseAttributeSyntax('textAttr::json.property')).to
  55. .throwWithCause(`Failed to parse syntax of attribute. Parse error at index 14:
  56. textAttr::json.property
  57. ^`);
  58. // "json.property" is treated as a modifier (which does not exist and will throw), not a JSON path
  59. expect(() => parseAttributeSyntax('textAttr:json.property')).to
  60. .throwWithCause(`Failed to parse syntax of attribute. Parse error at index 13:
  61. textAttr:json.property
  62. ^`);
  63. });
  64. it('parses JSON paths', () => {
  65. expect(parseAttributeSyntax('foo.bar')).to.deep.eq(sql.jsonPath(new Attribute('foo'), ['bar']));
  66. expect(parseAttributeSyntax('foo."bar"')).to.deep.eq(
  67. sql.jsonPath(new Attribute('foo'), ['bar']),
  68. );
  69. expect(parseAttributeSyntax('foo."bar\\""')).to.deep.eq(
  70. sql.jsonPath(new Attribute('foo'), ['bar"']),
  71. );
  72. expect(parseAttributeSyntax('foo."bar\\\\"')).to.deep.eq(
  73. sql.jsonPath(new Attribute('foo'), ['bar\\']),
  74. );
  75. expect(parseAttributeSyntax('foo[123]')).to.deep.eq(sql.jsonPath(new Attribute('foo'), [123]));
  76. expect(parseAttributeSyntax('foo."123"')).to.deep.eq(
  77. sql.jsonPath(new Attribute('foo'), ['123']),
  78. );
  79. expect(parseAttributeSyntax('foo.abc[0]."def"[1]')).to.deep.eq(
  80. sql.jsonPath(new Attribute('foo'), ['abc', 0, 'def', 1]),
  81. );
  82. });
  83. });
  84. describe('parseNestedJsonKeySyntax', () => {
  85. it('parses JSON paths', () => {
  86. expect(parseNestedJsonKeySyntax('foo.bar')).to.deep.eq({
  87. pathSegments: ['foo', 'bar'],
  88. castsAndModifiers: [],
  89. });
  90. expect(parseNestedJsonKeySyntax('abc-def.ijk-lmn')).to.deep.eq({
  91. pathSegments: ['abc-def', 'ijk-lmn'],
  92. castsAndModifiers: [],
  93. });
  94. expect(parseNestedJsonKeySyntax('"foo"."bar"')).to.deep.eq({
  95. pathSegments: ['foo', 'bar'],
  96. castsAndModifiers: [],
  97. });
  98. expect(parseNestedJsonKeySyntax('[0]')).to.deep.eq({
  99. pathSegments: [0],
  100. castsAndModifiers: [],
  101. });
  102. });
  103. it('parses casts and modifiers', () => {
  104. expect(parseNestedJsonKeySyntax('[0]:unquote::text:unquote::text')).to.deep.eq({
  105. pathSegments: [0],
  106. castsAndModifiers: [Unquote, 'text', Unquote, 'text'],
  107. });
  108. });
  109. });