candleValidation.test.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. const request = require('supertest');
  2. const { expect } = require('chai');
  3. const app = require('../src/server');
  4. const { getHistoricalCandles, validateCandles, generateTestCandles } = require('../services/candlestickService');
  5. describe('Candlestick API Tests', () => {
  6. const testSymbol = 'EURUSD';
  7. const testTimeframe = 'H1';
  8. describe('GET /api/prices/validate-candles/:symbol', () => {
  9. it('should validate candles for a valid symbol', async () => {
  10. const response = await request(app)
  11. .get(`/api/prices/validate-candles/${testSymbol}`)
  12. .query({ timeframe: testTimeframe, limit: 50 })
  13. .expect(200);
  14. expect(response.body.status).to.equal('success');
  15. expect(response.body.data).to.have.property('total');
  16. expect(response.body.data).to.have.property('valid');
  17. expect(response.body.data).to.have.property('invalid');
  18. expect(response.body.data.total).to.be.greaterThan(0);
  19. });
  20. it('should return 400 for missing symbol', async () => {
  21. const response = await request(app)
  22. .get('/api/prices/validate-candles/')
  23. .expect(404); // Express will return 404 for missing route param
  24. });
  25. it('should handle different timeframes', async () => {
  26. const timeframes = ['M1', 'M5', 'M15', 'H1', 'H4', 'D1'];
  27. for (const timeframe of timeframes) {
  28. const response = await request(app)
  29. .get(`/api/prices/validate-candles/${testSymbol}`)
  30. .query({ timeframe, limit: 10 })
  31. .expect(200);
  32. expect(response.body.status).to.equal('success');
  33. expect(response.body.timeframe).to.equal(timeframe);
  34. }
  35. });
  36. });
  37. describe('GET /api/prices/generate-test-candles/:symbol', () => {
  38. it('should generate test candles for a valid symbol', async () => {
  39. const response = await request(app)
  40. .get(`/api/prices/generate-test-candles/${testSymbol}`)
  41. .query({ timeframe: testTimeframe, count: 20 })
  42. .expect(200);
  43. expect(response.body.status).to.equal('success');
  44. expect(response.body.data).to.be.an('array');
  45. expect(response.body.data.length).to.equal(20);
  46. expect(response.body.validation).to.have.property('total', 20);
  47. });
  48. it('should validate generated test candles', async () => {
  49. const response = await request(app)
  50. .get(`/api/prices/generate-test-candles/${testSymbol}`)
  51. .query({ timeframe: testTimeframe, count: 10 })
  52. .expect(200);
  53. expect(response.body.meta.validationPassed).to.equal(true);
  54. expect(response.body.meta.successRate).to.equal('100.00%');
  55. });
  56. });
  57. describe('GET /api/prices/candle-info/:symbol', () => {
  58. it('should return candle statistics for a valid symbol', async () => {
  59. const response = await request(app)
  60. .get(`/api/prices/candle-info/${testSymbol}`)
  61. .query({ timeframe: testTimeframe })
  62. .expect(200);
  63. expect(response.body.status).to.equal('success');
  64. expect(response.body.data).to.have.property('count');
  65. expect(response.body.data).to.have.property('priceRange');
  66. expect(response.body.data).to.have.property('volume');
  67. expect(response.body.data.symbol).to.equal(testSymbol.toUpperCase());
  68. });
  69. });
  70. describe('Candlestick Service Unit Tests', () => {
  71. it('should generate valid test candles', () => {
  72. const candles = generateTestCandles(testSymbol, testTimeframe, 10);
  73. expect(candles).to.have.length(10);
  74. candles.forEach(candle => {
  75. expect(candle.symbol).to.equal(testSymbol);
  76. expect(candle.timeframe).to.equal(testTimeframe);
  77. expect(candle).to.have.property('open');
  78. expect(candle).to.have.property('high');
  79. expect(candle).to.have.property('low');
  80. expect(candle).to.have.property('close');
  81. expect(candle).to.have.property('volume');
  82. expect(candle).to.have.property('timestamp');
  83. });
  84. });
  85. it('should validate correct candles', () => {
  86. const validCandles = generateTestCandles(testSymbol, testTimeframe, 5);
  87. const results = validateCandles(validCandles);
  88. expect(results.total).to.equal(5);
  89. expect(results.valid).to.equal(5);
  90. expect(results.invalid).to.equal(0);
  91. expect(results.issues).to.have.length(0);
  92. });
  93. it('should detect invalid candles', () => {
  94. const invalidCandles = [
  95. {
  96. symbol: testSymbol,
  97. timeframe: testTimeframe,
  98. timestamp: new Date(),
  99. open: 'invalid',
  100. high: 1.1,
  101. low: 1.0,
  102. close: 1.05,
  103. volume: 1000
  104. },
  105. {
  106. symbol: testSymbol,
  107. timeframe: testTimeframe,
  108. timestamp: new Date(),
  109. open: 1.0,
  110. high: 0.9, // Invalid: high < open
  111. low: 1.0,
  112. close: 1.05,
  113. volume: 1000
  114. }
  115. ];
  116. const results = validateCandles(invalidCandles);
  117. expect(results.invalid).to.be.greaterThan(0);
  118. expect(results.issues.length).to.be.greaterThan(0);
  119. });
  120. });
  121. describe('GET /api/prices/historical/:symbol (Integration)', () => {
  122. it('should return historical data that can be validated', async () => {
  123. const response = await request(app)
  124. .get(`/api/prices/historical/${testSymbol}`)
  125. .query({ timeframe: testTimeframe, limit: 20 })
  126. .expect(200);
  127. expect(response.body.status).to.equal('success');
  128. expect(response.body.data).to.be.an('array');
  129. if (response.body.data.length > 0) {
  130. // Validate the returned candles
  131. const validationResults = validateCandles(response.body.data);
  132. expect(validationResults.total).to.equal(response.body.data.length);
  133. // Log validation results for debugging
  134. console.log(`Historical data validation: ${validationResults.valid}/${validationResults.total} valid candles`);
  135. }
  136. });
  137. });
  138. describe('Chart Data Format Tests', () => {
  139. it('should return data in correct format for charting', async () => {
  140. const response = await request(app)
  141. .get(`/api/prices/historical/${testSymbol}`)
  142. .query({ timeframe: testTimeframe, limit: 10 })
  143. .expect(200);
  144. expect(response.body.status).to.equal('success');
  145. expect(response.body.data).to.be.an('array');
  146. if (response.body.data.length > 0) {
  147. const candle = response.body.data[0];
  148. // Check required fields for charting
  149. expect(candle).to.have.property('timestamp');
  150. expect(candle).to.have.property('open');
  151. expect(candle).to.have.property('high');
  152. expect(candle).to.have.property('low');
  153. expect(candle).to.have.property('close');
  154. // Validate data types
  155. expect(typeof candle.open).to.equal('number');
  156. expect(typeof candle.high).to.equal('number');
  157. expect(typeof candle.low).to.equal('number');
  158. expect(typeof candle.close).to.equal('number');
  159. // Validate price logic
  160. expect(candle.high).to.be.at.least(Math.max(candle.open, candle.close));
  161. expect(candle.low).to.be.at.most(Math.min(candle.open, candle.close));
  162. }
  163. });
  164. });
  165. });
  166. // Helper function to wait for price updates
  167. const waitForPriceUpdate = (ms = 2000) => {
  168. return new Promise(resolve => setTimeout(resolve, ms));
  169. };
  170. module.exports = {
  171. waitForPriceUpdate
  172. };